Logic Wiki wikidb http://carol.dimap.ufrn.br/logicwiki/index.php/Main_Page MediaWiki 1.34.2 first-letter Media Special Talk User User talk Logic Wiki Logic Wiki talk File File talk MediaWiki MediaWiki talk Template Template talk Help Help talk Category Category talk Property Property talk Concept Concept talk smw/schema smw/schema talk Rule Rule talk Moodle - Documentação 0 2 3 2013-05-27T12:54:39Z Admin 1 Created page with " Setting and Course Administration * [[Creating Course]] * [[Enrol Participants on a Course]] * [[Manage Groups and Groupping]] * [[Adding a Activity]] : Adding a Activity:..." wikitext text/x-wiki Setting and Course Administration * [[Creating Course]] * [[Enrol Participants on a Course]] * [[Manage Groups and Groupping]] * [[Adding a Activity]] : [[Adding a Activity:Forum|Forum]], [[Adding a Activity:Chat|Chat]], [[Adding a Activity:Offline Activity|Offline Activity]], [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]], [[Adding a Activity:Assignment|Assignment]], [[Adding a Activity:Feedback|Feedback]], [[Adding a Activity:Quiz|Quiz]]. * [[Adding a Resource]] : [[Adding a Resource:Label|Label]], [[Adding a Resource:Files|Files]], [[Adding a Resource:External Tool|External Tool]], [[Adding a Resource:URL|URL]]. f6efa19ba1f290f62bbf20a52f03e7a372b38434 File:Criacao-de-curso 01.jpg 6 3 4 2013-05-27T13:06:33Z Admin 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Criacao-de-curso 02.jpg 6 4 5 2013-05-27T13:07:44Z Admin 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Criacao-de-curso 03.png 6 5 6 2013-05-27T13:11:47Z Admin 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Criacao-de-curso 04.png 6 6 7 2013-05-27T13:12:21Z Admin 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Criação de Curso 0 7 8 2013-05-27T13:18:27Z Admin 1 Created page with " Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", cli..." wikitext text/x-wiki Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", clique no item "Cursos": aparecerão opções de configuração e de adição/modificação de cursos. Clique em "Acrescentar/modificar cursos". [[File:Criacao-de-curso_01.jpg]] ‎ Após o segundo clique, abrirá uma página de criação/edição de curso. Nesta página, os cursos são organizados por tipo, mostrados na tabela como "categorias de cursos". Mas como o objetivo é criar um curso, e não modificar um já existente, basta clicar no botão correspondente, que está fora da tabela. [[File:Criacao-de-curso_02.jpg]] Após clicar no botão para criar um curso, abrirá uma outra página, esta sendo para configuração do curso que será criado. Basicamente, só é preciso preencher os campos obrigatórios para criar, de fato, um curso. Esses campos obrigatórios são: o nome do curso (não existe um curso sem uma identificação), e um "nome breve do curso", que é nada mais do que uma abreviação, ou um código,para o nome do curso - como LIBRAS é uma abreviação para Língua Brasileira de Sinais. Apesar de só serem necessários os campos obrigatórios, é importante que se configure ao menos o formato do curso, que indica como o curso será organizado. No formato de tópicos, por exemplo, o curso será mostrado em, obviamente, tópicos, o que ajuda a ter um melhor controle de como o curso será organizado. Outro ponto importante é a configuração de grupos. A configuração controla a visibilidade dos grupos em relação aos outros grupos. Na "modalidade grupo", define-se como os grupos ficam visíveis uns aos outros: nenhum grupo - todos se vêem, grupos separados - o grupo vê somente as atividades de seu grupo, grupos visíveis - os membros de um grupo podem ver os outros grupos. No campo "forçar modalidade grupo", se a opção for sim, as atividades em grupos são forçadas a ter a mesma configuração de grupos do curso, e em caso contrário, as atividades podem ser configuradas de uma forma diferente do curso. Na configuração de disponibilidade, escolhemos se o curso aparece ou não na página de cursos. Ao fim da configuração, basta clicar em "salvar mudanças", e então o curso será criado! :-) [[File:Criacao-de-curso_03.png]] [[File:Criacao-de-curso_04.png]] a249417ba999090ecd374f33fa2cae189e9a1975 9 8 2013-05-27T13:22:27Z Admin 1 wikitext text/x-wiki Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", clique no item "Cursos": aparecerão opções de configuração e de adição/modificação de cursos. Clique em "Acrescentar/modificar cursos". [[File:Criacao-de-curso_01.jpg|1040px]] ‎ Após o segundo clique, abrirá uma página de criação/edição de curso. Nesta página, os cursos são organizados por tipo, mostrados na tabela como "categorias de cursos". Mas como o objetivo é criar um curso, e não modificar um já existente, basta clicar no botão correspondente, que está fora da tabela. [[File:Criacao-de-curso_02.jpg|1040px]] Após clicar no botão para criar um curso, abrirá uma outra página, esta sendo para configuração do curso que será criado. Basicamente, só é preciso preencher os campos obrigatórios para criar, de fato, um curso. Esses campos obrigatórios são: o nome do curso (não existe um curso sem uma identificação), e um "nome breve do curso", que é nada mais do que uma abreviação, ou um código,para o nome do curso - como LIBRAS é uma abreviação para Língua Brasileira de Sinais. Apesar de só serem necessários os campos obrigatórios, é importante que se configure ao menos o formato do curso, que indica como o curso será organizado. No formato de tópicos, por exemplo, o curso será mostrado em, obviamente, tópicos, o que ajuda a ter um melhor controle de como o curso será organizado. Outro ponto importante é a configuração de grupos. A configuração controla a visibilidade dos grupos em relação aos outros grupos. Na "modalidade grupo", define-se como os grupos ficam visíveis uns aos outros: nenhum grupo - todos se vêem, grupos separados - o grupo vê somente as atividades de seu grupo, grupos visíveis - os membros de um grupo podem ver os outros grupos. No campo "forçar modalidade grupo", se a opção for sim, as atividades em grupos são forçadas a ter a mesma configuração de grupos do curso, e em caso contrário, as atividades podem ser configuradas de uma forma diferente do curso. Na configuração de disponibilidade, escolhemos se o curso aparece ou não na página de cursos. Ao fim da configuração, basta clicar em "salvar mudanças", e então o curso será criado! :-) [[File:Criacao-de-curso_03.png|1040px]] [[File:Criacao-de-curso_04.png|1040px]] 1ce014ea484e765908823aec8fea3efba76bc209 10 9 2013-05-27T13:23:55Z Admin 1 wikitext text/x-wiki Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", clique no item "Cursos": aparecerão opções de configuração e de adição/modificação de cursos. Clique em "Acrescentar/modificar cursos". [[File:Criacao-de-curso_01.jpg|1040px]] ‎ Após o segundo clique, abrirá uma página de criação/edição de curso. Nesta página, os cursos são organizados por tipo, mostrados na tabela como "categorias de cursos". Mas como o objetivo é criar um curso, e não modificar um já existente, basta clicar no botão correspondente, que está fora da tabela. [[File:Criacao-de-curso_02.jpg|1040px]] Após clicar no botão para criar um curso, abrirá uma outra página, esta sendo para configuração do curso que será criado. Basicamente, só é preciso preencher os campos obrigatórios para criar, de fato, um curso. Esses campos obrigatórios são: o nome do curso (não existe um curso sem uma identificação), e um "nome breve do curso", que é nada mais do que uma abreviação, ou um código,para o nome do curso - como LIBRAS é uma abreviação para Língua Brasileira de Sinais. Apesar de só serem necessários os campos obrigatórios, é importante que se configure ao menos o formato do curso, que indica como o curso será organizado. No formato de tópicos, por exemplo, o curso será mostrado em, obviamente, tópicos, o que ajuda a ter um melhor controle de como o curso será organizado. Outro ponto importante é a configuração de grupos. A configuração controla a visibilidade dos grupos em relação aos outros grupos. Na "modalidade grupo", define-se como os grupos ficam visíveis uns aos outros: nenhum grupo - todos se vêem, grupos separados - o grupo vê somente as atividades de seu grupo, grupos visíveis - os membros de um grupo podem ver os outros grupos. No campo "forçar modalidade grupo", se a opção for sim, as atividades em grupos são forçadas a ter a mesma configuração de grupos do curso, e em caso contrário, as atividades podem ser configuradas de uma forma diferente do curso. Na configuração de disponibilidade, escolhemos se o curso aparece ou não na página de cursos. [[File:Criacao-de-curso_03.png|1040px]] Ao fim da configuração, basta clicar em "salvar mudanças", e então o curso será criado! :-) [[File:Criacao-de-curso_04.png|1040px]] 4ac64edaf6ea58de44af185de0fbb4246e595e2d 11 10 2013-05-27T13:25:19Z Admin 1 wikitext text/x-wiki Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", clique no item "Cursos": aparecerão opções de configuração e de adição/modificação de cursos. Clique em "Acrescentar/modificar cursos". [[File:Criacao-de-curso_01.jpg|1040px]] Após o segundo clique, abrirá uma página de criação/edição de curso. Nesta página, os cursos são organizados por tipo, mostrados na tabela como "categorias de cursos". Mas como o objetivo é criar um curso, e não modificar um já existente, basta clicar no botão correspondente, que está fora da tabela. [[File:Criacao-de-curso_02.jpg|1040px]] Após clicar no botão para criar um curso, abrirá uma outra página, esta sendo para configuração do curso que será criado. Basicamente, só é preciso preencher os campos obrigatórios para criar, de fato, um curso. Esses campos obrigatórios são: o nome do curso (não existe um curso sem uma identificação), e um "nome breve do curso", que é nada mais do que uma abreviação, ou um código,para o nome do curso - como LIBRAS é uma abreviação para Língua Brasileira de Sinais. Apesar de só serem necessários os campos obrigatórios, é importante que se configure ao menos o formato do curso, que indica como o curso será organizado. No formato de tópicos, por exemplo, o curso será mostrado em, obviamente, tópicos, o que ajuda a ter um melhor controle de como o curso será organizado. Outro ponto importante é a configuração de grupos. A configuração controla a visibilidade dos grupos em relação aos outros grupos. Na "modalidade grupo", define-se como os grupos ficam visíveis uns aos outros: nenhum grupo - todos se vêem, grupos separados - o grupo vê somente as atividades de seu grupo, grupos visíveis - os membros de um grupo podem ver os outros grupos. No campo "forçar modalidade grupo", se a opção for sim, as atividades em grupos são forçadas a ter a mesma configuração de grupos do curso, e em caso contrário, as atividades podem ser configuradas de uma forma diferente do curso. Na configuração de disponibilidade, escolhemos se o curso aparece ou não na página de cursos. [[File:Criacao-de-curso_03.png|1040px]] Ao fim da configuração, basta clicar em "salvar mudanças", e então o curso será criado! :-) [[File:Criacao-de-curso_04.png|1040px]] b52abb125caa4ac5a677e01633f091dc806bb137 File:Grupo1.jpg 6 8 12 2013-05-27T15:10:04Z Admin 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Grupos e Agrupamentos 0 9 13 2013-05-27T15:19:29Z Admin 1 Created page with " == Introdução == Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois..." wikitext text/x-wiki == Introdução == Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' [[File:grupo1.jpg|700px]] == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. [[File:grupo2.jpg|700px]] Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. [[File:grupo3.jpg|700px]] == Criando grupos - Automático == [[File:grupo4.jpg|700px]] A opção em vermelho determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). A opção em laranja determina de qual grupo global seram selecionados os usuários. A opção em amarelo define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. A opção em verde, define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). A opção em azul define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir [[File:grupo5.jpg|700px]] Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir [[File:grupo6.jpg|700px]] A opção em roxo, define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. [[File:grupo7.jpg|700px]] == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). [[File:grupo8.jpg|700px]] Resultando em: [[File:grupo9.jpg|700px]] Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir [[File:grupo10.jpg|700px]] Resultando em: [[File:grupo11.jpg|700px]] == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir [[File:grupo12.jpg|700px]] Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir [[File:grupo13.jpg|700px]] 79d868db9756aa6af806865a56f87969fe05cc00 14 13 2013-05-27T15:19:43Z Admin 1 /* Introdução */ wikitext text/x-wiki == Introdução == Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' [[File:grupo1.jpg|200px]] == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. [[File:grupo2.jpg|700px]] Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. [[File:grupo3.jpg|700px]] == Criando grupos - Automático == [[File:grupo4.jpg|700px]] A opção em vermelho determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). A opção em laranja determina de qual grupo global seram selecionados os usuários. A opção em amarelo define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. A opção em verde, define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). A opção em azul define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir [[File:grupo5.jpg|700px]] Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir [[File:grupo6.jpg|700px]] A opção em roxo, define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. [[File:grupo7.jpg|700px]] == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). [[File:grupo8.jpg|700px]] Resultando em: [[File:grupo9.jpg|700px]] Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir [[File:grupo10.jpg|700px]] Resultando em: [[File:grupo11.jpg|700px]] == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir [[File:grupo12.jpg|700px]] Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir [[File:grupo13.jpg|700px]] 0c0b7761b31140c4e0c8ce43a9d67be6be6cdc3c 29 14 2013-05-27T15:22:18Z Admin 1 /* Criando grupos - Automático */ wikitext text/x-wiki == Introdução == Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' [[File:grupo1.jpg|200px]] == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. [[File:grupo2.jpg|700px]] Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. [[File:grupo3.jpg|700px]] == Criando grupos - Automático == [[File:grupo4.jpg|700px]] * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir [[File:grupo5.jpg|700px]] Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir [[File:grupo6.jpg|700px]] * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. [[File:grupo7.jpg|700px]] == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). [[File:grupo8.jpg|700px]] Resultando em: [[File:grupo9.jpg|700px]] Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir [[File:grupo10.jpg|700px]] Resultando em: [[File:grupo11.jpg|700px]] == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir [[File:grupo12.jpg|700px]] Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir [[File:grupo13.jpg|700px]] 1a58961e87ebaa3c4ef9cba80ebf0272c6962404 30 29 2013-05-27T15:25:39Z Admin 1 wikitext text/x-wiki == Introdução == Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' {| border="1" [[File:grupo1.jpg|right|200px]] |} == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. [[File:grupo2.jpg|700px]] Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. [[File:grupo3.jpg|700px]] == Criando grupos - Automático == [[File:grupo4.jpg|700px]] * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir [[File:grupo5.jpg|700px]] Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir [[File:grupo6.jpg|700px]] * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. [[File:grupo7.jpg|700px]] == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). [[File:grupo8.jpg|700px]] Resultando em: [[File:grupo9.jpg|700px]] Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir [[File:grupo10.jpg|700px]] Resultando em: [[File:grupo11.jpg|700px]] == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir [[File:grupo12.jpg|700px]] Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir [[File:grupo13.jpg|700px]] 515f62550b727ff9b831da96f40e986a9e6625aa 31 30 2013-05-27T15:27:26Z Admin 1 wikitext text/x-wiki == Introdução == {| border="1" [[File:grupo1.jpg|right|200px]] |} Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' {| border="1" [[File:grupo1.jpg|right|200px]] |} == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. {| border="1" [[File:grupo2.jpg|700px]] |} Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. {| border="1" [[File:grupo3.jpg|700px]] |} == Criando grupos - Automático == {| border="1" [[File:grupo4.jpg|700px]] |} * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir {| border="1" [[File:grupo5.jpg|700px]] |} Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir {| border="1" [[File:grupo6.jpg|700px]] |} * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. {| border="1" [[File:grupo7.jpg|700px]] |} == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). {| border="1" [[File:grupo8.jpg|700px]] |} Resultando em: [[File:grupo9.jpg|700px]] Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir [[File:grupo10.jpg|700px]] Resultando em: [[File:grupo11.jpg|700px]] == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir [[File:grupo12.jpg|700px]] Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir [[File:grupo13.jpg|700px]] 929c318571f6b9c9779ec7ab2d729eb3e7481772 32 31 2013-05-27T15:27:52Z Admin 1 wikitext text/x-wiki == Introdução == {| border="1" [[File:grupo1.jpg|right|200px]] |} Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. {| border="1" [[File:grupo2.jpg|700px]] |} Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. {| border="1" [[File:grupo3.jpg|700px]] |} == Criando grupos - Automático == {| border="1" [[File:grupo4.jpg|700px]] |} * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir {| border="1" [[File:grupo5.jpg|700px]] |} Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir {| border="1" [[File:grupo6.jpg|700px]] |} * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. {| border="1" [[File:grupo7.jpg|700px]] |} == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). {| border="1" [[File:grupo8.jpg|700px]] |} Resultando em: [[File:grupo9.jpg|700px]] Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir [[File:grupo10.jpg|700px]] Resultando em: [[File:grupo11.jpg|700px]] == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir [[File:grupo12.jpg|700px]] Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir [[File:grupo13.jpg|700px]] 3afbfb2044ae3798f31c8bec53ce5b3964afa7ce 33 32 2013-05-27T15:31:20Z Admin 1 wikitext text/x-wiki == Introdução == {| border="1" |[[File:grupo1.jpg|right|200px]] |} Essa é uma apresentação para mostrar como utilizar agrupamentos no moodle, primeiro será mostrado como criar grupos e como criar agrupamentos, depois como utiliza-los nas atividades. Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. {| border="1" |[[File:grupo2.jpg|700px]] |} Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. {| border="1" |[[File:grupo3.jpg|700px]] |} == Criando grupos - Automático == {| border="1" |[[File:grupo4.jpg|700px]] |} * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir {| border="1" | [[File:grupo5.jpg|700px]] |} Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir {| border="1" | [[File:grupo6.jpg|700px]] |} * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. {| border="1" | [[File:grupo7.jpg|700px]] |} == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). {| border="1" | [[File:grupo8.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo9.jpg|700px]] |} Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir {| border="1" |[[File:grupo10.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo11.jpg|700px]] |} == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir {| border="1" |[[File:grupo12.jpg|700px]] |} Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir {| border="1" |[[File:grupo13.jpg|700px]] |} 8bc4eb11c111e54b4981365fd5712bc1aef94608 34 33 2013-05-27T15:38:05Z Admin 1 wikitext text/x-wiki == Introdução == Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' {| border="1" |[[File:grupo1.jpg|right|200px]] |} == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. {| border="1" |[[File:grupo2.jpg|700px]] |} Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. {| border="1" |[[File:grupo3.jpg|700px]] |} == Criando grupos - Automático == {| border="1" |[[File:grupo4.jpg|700px]] |} * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir {| border="1" | [[File:grupo5.jpg|700px]] |} Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir {| border="1" | [[File:grupo6.jpg|700px]] |} * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. {| border="1" | [[File:grupo7.jpg|700px]] |} == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). {| border="1" | [[File:grupo8.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo9.jpg|700px]] |} Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir {| border="1" |[[File:grupo10.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo11.jpg|700px]] |} == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir {| border="1" |[[File:grupo12.jpg|700px]] |} Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * Nenhum grupo - Não há sub-grupos, todos fazem parte de uma grande comunidade. * Grupos separados - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * Grupos visíveis - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir {| border="1" |[[File:grupo13.jpg|700px]] |} 4219cffb333eee83bc29bfa96ddf41533cabac01 35 34 2013-05-27T15:38:35Z Admin 1 /* Criando atividade com o agrupamento */ wikitext text/x-wiki == Introdução == Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' {| border="1" |[[File:grupo1.jpg|right|200px]] |} == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. {| border="1" |[[File:grupo2.jpg|700px]] |} Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. {| border="1" |[[File:grupo3.jpg|700px]] |} == Criando grupos - Automático == {| border="1" |[[File:grupo4.jpg|700px]] |} * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir {| border="1" | [[File:grupo5.jpg|700px]] |} Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir {| border="1" | [[File:grupo6.jpg|700px]] |} * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. {| border="1" | [[File:grupo7.jpg|700px]] |} == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). {| border="1" | [[File:grupo8.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo9.jpg|700px]] |} Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir {| border="1" |[[File:grupo10.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo11.jpg|700px]] |} == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir {| border="1" |[[File:grupo12.jpg|700px]] |} Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * '''Nenhum grupo''' - Não há sub-grupos, todos fazem parte de uma grande comunidade. * '''Grupos separados''' - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * '''Grupos visíveis''' - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir {| border="1" |[[File:grupo13.jpg|700px]] |} cba2cd58a4eb81d9f9849834ea7675e95e5ae72f File:Grupo15.jpg 6 10 15 2013-05-27T15:20:14Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo14.jpg 6 11 16 2013-05-27T15:20:15Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo13.jpg 6 12 17 2013-05-27T15:20:15Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo12.jpg 6 13 18 2013-05-27T15:20:16Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo11.jpg 6 14 19 2013-05-27T15:20:16Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo10.jpg 6 15 20 2013-05-27T15:20:17Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo9.jpg 6 16 21 2013-05-27T15:20:17Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo8.jpg 6 17 22 2013-05-27T15:20:18Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo7.jpg 6 18 23 2013-05-27T15:20:18Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo6.jpg 6 19 24 2013-05-27T15:20:18Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo5.jpg 6 20 25 2013-05-27T15:20:19Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo4.jpg 6 21 26 2013-05-27T15:20:19Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo3.jpg 6 22 27 2013-05-27T15:20:20Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Grupo2.jpg 6 23 28 2013-05-27T15:20:20Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Adicionando uma Atividade 0 24 36 2013-05-27T15:43:09Z Admin 1 Created page with " * [[Adding a Activity:Forum|Forum]] * [[Adding a Activity:Chat|Chat]] * [[Adding a Activity:Offline Activity|Offline Activity]] * Adding a Activity:Assignment - Upload a s..." wikitext text/x-wiki * [[Adding a Activity:Forum|Forum]] * [[Adding a Activity:Chat|Chat]] * [[Adding a Activity:Offline Activity|Offline Activity]] * [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]] * [[Adding a Activity:Assignment|Assignment]] * [[Adding a Activity:Feedback|Feedback]] * [[Adding a Activity:Quiz|Quiz]] 83fc55d5c3255a75944e1f37814be6ee890652b0 File:Ativar edicao.png 6 25 37 2013-05-27T15:44:53Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Fórum 0 26 38 2013-05-27T15:48:00Z Admin 1 Created page with " Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[:File:ativar_edicao.png]]" wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[:File:ativar_edicao.png]] edc4672f26824e28a7ccc8cae41dbdfd9e4e3dd3 39 38 2013-05-27T15:48:27Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[File:ativar_edicao.png]] 5b05c6b7ec6a8ce0e9563f14bc3e18a1e29cd5a4 40 39 2013-05-27T15:48:45Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[File:ativar_edicao.png|700px]] 30e292746976e31edd344fc44f4f7a314e4bec98 41 40 2013-05-27T15:49:00Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[File:ativar_edicao.png|800px]] 4b7167710785d540706c2bd4d1bbe482f8bdf198 42 41 2013-05-27T15:49:13Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[File:ativar_edicao.png|900px]] 0dd63147226d415244f9fe90e9a6f9dc259792a3 43 42 2013-05-27T15:49:43Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[File:ativar_edicao.png|1040px]] 7ff62d43f8167af3267c34ba80c2437e43c31625 46 43 2013-05-27T15:54:05Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. [[File:ativar_edicao.png|1040px]] Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Add an activity or resource", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. [[File:adicao-de-atividade-forum.png|1040px]] Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). [[File:adicao-de-atividade-forum_02.png|1040px]] Depois de configurar o fórum, salve-o. [[File:adicao-de-atividade-forum_03.png|1040px]] bbe70c45ec79d471a072f4f49f9518a3c5ef531e 47 46 2013-05-27T15:55:15Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. {| border="1" |[[File:ativar_edicao.png|1040px]] Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Add an activity or resource", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. {| border="1" |[[File:adicao-de-atividade-forum.png|1040px]] |} Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). {| border="1" |[[File:adicao-de-atividade-forum_02.png|1040px]] |} Depois de configurar o fórum, salve-o. {| border="1" |[[File:adicao-de-atividade-forum_03.png|1040px]] |} b2b6c01fcde0951ae8d1e13387fd1d3a156a42b8 48 47 2013-05-27T15:55:26Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Add an activity or resource", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. {| border="1" |[[File:adicao-de-atividade-forum.png|1040px]] |} Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). {| border="1" |[[File:adicao-de-atividade-forum_02.png|1040px]] |} Depois de configurar o fórum, salve-o. {| border="1" |[[File:adicao-de-atividade-forum_03.png|1040px]] |} d66fdbf515823d9f0ef4c8834e40525deaa1871f 49 48 2013-05-27T15:55:44Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Add an activity or resource", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. {| border="1" |[[File:adicao-de-atividade-forum.png|1040px]] |} Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). {| border="1" |[[File:adicao-de-atividade-forum_02.png|1040px]] |} Depois de configurar o fórum, salve-o. {| border="1" |[[File:adicao-de-atividade-forum_03.png|1040px]] |} ff6d8d6241a59fe4d6e4abe552a3cd847fe88c18 50 49 2013-05-27T15:56:01Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Add an activity or resource", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. {| border="1" |[[File:adicao-de-atividade-forum.png|1040px]] |} Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). {| border="1" |[[File:adicao-de-atividade-forum_02.png|1040px]] |} Depois de configurar o fórum, salve-o. {| border="1" |[[File:adicao-de-atividade-forum_03.png|1040px]] |} 365c11672883ccf24329d0e2727456b2c68c366f File:Adicao-de-atividade-forum.png 6 27 44 2013-05-27T15:51:34Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividade-forum 02.png 6 28 45 2013-05-27T15:52:51Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividade-forum 03.png 6 29 51 2013-05-27T15:56:29Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-chat 03.png 6 30 52 2013-05-27T16:03:34Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-chat 02.png 6 31 53 2013-05-27T16:03:35Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-chat 01.png 6 32 54 2013-05-27T16:03:36Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Chat 0 33 55 2013-05-27T16:04:33Z Admin 1 Created page with " Da mesma forma como no fórum, para adicionar um chat clicamos em "Add an activity or resource", e então selecionamos a opção "Chat". Então, basta configurar e salvar o ..." wikitext text/x-wiki Da mesma forma como no fórum, para adicionar um chat clicamos em "Add an activity or resource", e então selecionamos a opção "Chat". Então, basta configurar e salvar o chat. {| border="1" |[[File:ativar_edicao.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-chat_01.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-chat_02.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-chat_03.png|1040px]] |} d890205d1d27b58bb7692a1094abf98feaa44410 56 55 2013-05-27T16:07:18Z Admin 1 wikitext text/x-wiki Antes de tudo é preciso primeiro ativar o modo de edição: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Da mesma forma como no fórum, para adicionar um chat clicamos em "Add an activity or resource": e então selecionamos a opção "Chat". e salvar o chat. {| border="1" |[[File:adicao-de-atividades-chat_01.png|1040px]] |} Então, basta '''configurar''': {| border="1" |[[File:adicao-de-atividades-chat_02.png|1040px]] |} E salvar em seguida: {| border="1" |[[File:adicao-de-atividades-chat_03.png|1040px]] |} bacefd36eb6582b9f4e9774848d659d23a126741 57 56 2013-05-27T16:07:29Z Admin 1 wikitext text/x-wiki Antes de tudo é preciso primeiro ativar o modo de edição: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Da mesma forma como no fórum, para adicionar um chat clicamos em "Add an activity or resource": e então selecionamos a opção "Chat". e salvar o chat. {| border="1" |[[File:adicao-de-atividades-chat_01.png|1040px]] |} Então, basta '''configurar''': {| border="1" |[[File:adicao-de-atividades-chat_02.png|1040px]] |} E salvar em seguida: {| border="1" |[[File:adicao-de-atividades-chat_03.png|1040px]] |} 7413cc065f03ae9235d3fc1aff029cbe33a51544 58 57 2013-05-27T16:07:44Z Admin 1 wikitext text/x-wiki Antes de tudo é preciso primeiro ativar o modo de edição: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Da mesma forma como no fórum, para adicionar um chat clicamos em "Add an activity or resource": e então selecionamos a opção "Chat". e salvar o chat. {| border="1" |[[File:adicao-de-atividades-chat_01.png|1040px]] |} Então, basta '''configurar''': {| border="1" |[[File:adicao-de-atividades-chat_02.png|1040px]] |} E salvar em seguida: {| border="1" |[[File:adicao-de-atividades-chat_03.png|1040px]] |} 2045feafcf1ea7cf78ea8a1e7b0d9f3add0828e4 81 58 2013-05-27T16:42:39Z Admin 1 Admin moved page [[Adding a Activity:Chat]] to [[Chat]] wikitext text/x-wiki Antes de tudo é preciso primeiro ativar o modo de edição: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Da mesma forma como no fórum, para adicionar um chat clicamos em "Add an activity or resource": e então selecionamos a opção "Chat". e salvar o chat. {| border="1" |[[File:adicao-de-atividades-chat_01.png|1040px]] |} Então, basta '''configurar''': {| border="1" |[[File:adicao-de-atividades-chat_02.png|1040px]] |} E salvar em seguida: {| border="1" |[[File:adicao-de-atividades-chat_03.png|1040px]] |} 2045feafcf1ea7cf78ea8a1e7b0d9f3add0828e4 File:Adicao-de-atividades-atividades-offline 03.png 6 34 59 2013-05-27T16:09:51Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-atividades-offline 02.png 6 35 60 2013-05-27T16:09:52Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-atividades-offline 01.png 6 36 61 2013-05-27T16:09:52Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Atividade Offline 0 37 62 2013-05-27T16:30:24Z Admin 1 Created page with " Do mesmo modo como fizemos com as outras atividades, clicamos em "Add an activity or resource", e então selecionamos a opção "Atividade Offline". File:adicao-de-ativ..." wikitext text/x-wiki Do mesmo modo como fizemos com as outras atividades, clicamos em "Add an activity or resource", e então selecionamos a opção "Atividade Offline". [[File:adicao-de-atividades-atividades-offline_01.png|1040px]] Depois, aparecerá uma página de configuração da atividade. Basicamente, precisamos de um nome e uma descrição para a tarefa. [[File:adicao-de-atividades-atividades-offline_02.png|1040px]] Então, configuramos o período visualizável da tarefa e como será dada a nota da tarefa. Além disso, há a configuração dos grupos, que funciona da mesma forma como na configuração das outras atividades. [[File:adicao-de-atividades-atividades-offline_03.png|1040px]] 84ddd7ee8fc8d620e581d7510d9b36e63490cb03 78 62 2013-05-27T16:40:46Z Admin 1 Admin moved page [[Adding a Activity:Offline Activity]] to [[Atividade Offline]] wikitext text/x-wiki Do mesmo modo como fizemos com as outras atividades, clicamos em "Add an activity or resource", e então selecionamos a opção "Atividade Offline". [[File:adicao-de-atividades-atividades-offline_01.png|1040px]] Depois, aparecerá uma página de configuração da atividade. Basicamente, precisamos de um nome e uma descrição para a tarefa. [[File:adicao-de-atividades-atividades-offline_02.png|1040px]] Então, configuramos o período visualizável da tarefa e como será dada a nota da tarefa. Além disso, há a configuração dos grupos, que funciona da mesma forma como na configuração das outras atividades. [[File:adicao-de-atividades-atividades-offline_03.png|1040px]] 84ddd7ee8fc8d620e581d7510d9b36e63490cb03 Grupos e Agrupamentos 0 9 63 35 2013-05-27T16:33:17Z Admin 1 Admin moved page [[Manage Groups and Groupping]] to [[Grupos e Agrupamentos]] wikitext text/x-wiki == Introdução == Os grupos no moodle não contam como uma atividade em grupo, mas sim uma forma de separar os alunos para melhor visualização do desempenho de cada grupo, como por exemplo várias turmas em um mesmo curso. '''Trata-se de um recurso muito útil para distribuir atividades diferentes para diferentes agrupamentos. Com a opção de agrupamente é podemos oferecer um mesmo curso para duas turmas com atividades distintas. ''' {| border="1" |[[File:grupo1.jpg|right|200px]] |} == Criando grupos == Para criar grupos e agrupamentos, primeiro deve-se ir em "configurações", depois em "usuários", e selecionar a opção "grupos", como mostrado na imagem ao lado. {| border="1" |[[File:grupo2.jpg|700px]] |} Ao chegar na tela anterior você pode gerar os grupos manualmente ou automáticamente. Mostrarei primeiro como funciona a opção de gerar automaticamente. Assim que entrar na tela a seguir selecione a opção "Mostrar avançado" como está ilustrado. {| border="1" |[[File:grupo3.jpg|700px]] |} == Criando grupos - Automático == {| border="1" |[[File:grupo4.jpg|700px]] |} * '''Em vermelho''' determina que tipo de usuário serão colocados nos grupos (estudantes, professores, ou todos). * '''Em laranja''' determina de qual grupo global seram selecionados os usuários. * '''Em amarelo''' define como os grupos serão distribuídos (se você vai restringir a quantidade de membros por grupo, ou a quantidade de grupos), depois de selecionada a opção, coloca-se a quantidade desejada na caixinha abaixo. * '''Em verde''', define o critério de seleção dos grupos (aleatório, ordem alfabética, ordem de id, etc). * '''Em azul''' define como serão nomeados os grupos, (o caractere "@" nomeia os grupos de A-Z, enquanto que o caractere "#" nomeia por números, a palavra "Grupo" na imagem será o texto que vai ter no nome de todos os grupos). Caso você deseje definir os grupos e os membros de cada grupo precisará criar os grupos e os agrupamentos manualmente. Criaremos primeiro os grupo, para isso selecione a opção "criar grupo", indo para a tela mostrada a seguir {| border="1" | [[File:grupo5.jpg|700px]] |} Depois de criado os grupos, você precisará inserir os membros neles, para isso selecione o grupo e depois clique em "Adicionar/remover usuários" como mostrado na tela a seguir {| border="1" | [[File:grupo6.jpg|700px]] |} * '''Em roxo''', define se os grupos gerados vão ser colocados em um novo agrupamento(nesse caso informa o nome do novo agrupamento), ou em um já existente. {| border="1" | [[File:grupo7.jpg|700px]] |} == Criando agrupamentos == Depois de criado os grupos e adicionado os membros em cada grupo, é preciso criar um agrupamento com os grupos para poder usá-los nas atividades. Para criar um agrupamento é só clicar na opção "criar agrupamento" na aba "Agrupamentos" mostrado na tela a seguir (a seleção em verde foi o agrupamento gerado de forma automática usando o método mostrado anteriormente). {| border="1" | [[File:grupo8.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo9.jpg|700px]] |} Depois de criado o agrupamento, é preciso adicionar os grupos a ele, para isso clique nos "três bonequinhos" como mostrado na tela a seguir {| border="1" |[[File:grupo10.jpg|700px]] |} '''Resultando em:''' {| border="1" |[[File:grupo11.jpg|700px]] |} == Criando atividade com o agrupamento == Depois de criado o agrupamento com os grupos, para utilizá-lo em uma atividade basta durante criação da mesma, na aba "configurações comuns de módulos" (a última), selecione para mostrar as configurações avançadas e será exibida a tela mostrada a seguir {| border="1" |[[File:grupo12.jpg|700px]] |} Na opção de agrupamento, selecione o agrupamento que você criou. Para que apenas os membros colocados nos grupos possam ver a atividade, selecione a caixa "Disponível apenas para membros do grupo" A primeira opção (Modalidade grupo) tem as seguintes opções: * '''Nenhum grupo''' - Não há sub-grupos, todos fazem parte de uma grande comunidade. * '''Grupos separados''' - Cada membro de grupo pode ver apenas seu próprio grupos, os outros são invisíveis. * '''Grupos visíveis''' - Cada membro do grupo trabalha no seu próprio grupo mas pode também ver outros grupos. == Visualizando os grupos == Depois de criada a atividade e configurada ela para usar o agrupamento, ao entrar na atividade você terá a opção de olhar os envios de todos ou apenas o de cada grupo separadamente. Nesse tutorial foi utilizada como exemplo a tarefa de envio único, assim se quiser ver os envios e já dar uma nota e um feedback para o aluno, basta clicar em "tentativas nesta tarefa" que inicialmente estará como "nenhuma tentativa nesta tarefa", veja as imagens a seguir {| border="1" |[[File:grupo13.jpg|700px]] |} cba2cd58a4eb81d9f9849834ea7675e95e5ae72f Manage Groups and Groupping 0 38 64 2013-05-27T16:33:17Z Admin 1 Admin moved page [[Manage Groups and Groupping]] to [[Grupos e Agrupamentos]] wikitext text/x-wiki #REDIRECT [[Grupos e Agrupamentos]] 7dda63342fae0886eaa2bdf947160e5f14e3362f Moodle - Documentação 0 2 65 3 2013-05-27T16:33:43Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Creating Course]] * [[Enrol Participants on a Course]] * [[Grupos e Agrupamentos]] * [[Adding a Activity]] : [[Adding a Activity:Forum|Forum]], [[Adding a Activity:Chat|Chat]], [[Adding a Activity:Offline Activity|Offline Activity]], [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]], [[Adding a Activity:Assignment|Assignment]], [[Adding a Activity:Feedback|Feedback]], [[Adding a Activity:Quiz|Quiz]]. * [[Adding a Resource]] : [[Adding a Resource:Label|Label]], [[Adding a Resource:Files|Files]], [[Adding a Resource:External Tool|External Tool]], [[Adding a Resource:URL|URL]]. f26164e8702c6335fd099e3316ba7521ced4ac7c 66 65 2013-05-27T16:34:15Z Admin 1 Admin moved page [[Moodle Documentation Index]] to [[Moodle - Documentação]] wikitext text/x-wiki Setting and Course Administration * [[Creating Course]] * [[Enrol Participants on a Course]] * [[Grupos e Agrupamentos]] * [[Adding a Activity]] : [[Adding a Activity:Forum|Forum]], [[Adding a Activity:Chat|Chat]], [[Adding a Activity:Offline Activity|Offline Activity]], [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]], [[Adding a Activity:Assignment|Assignment]], [[Adding a Activity:Feedback|Feedback]], [[Adding a Activity:Quiz|Quiz]]. * [[Adding a Resource]] : [[Adding a Resource:Label|Label]], [[Adding a Resource:Files|Files]], [[Adding a Resource:External Tool|External Tool]], [[Adding a Resource:URL|URL]]. f26164e8702c6335fd099e3316ba7521ced4ac7c 74 66 2013-05-27T16:37:50Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Enrol Participants on a Course]] * [[Grupos e Agrupamentos]] * [[Adding a Activity]] : [[Adding a Activity:Forum|Forum]], [[Adding a Activity:Chat|Chat]], [[Adding a Activity:Offline Activity|Offline Activity]], [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]], [[Adding a Activity:Assignment|Assignment]], [[Adding a Activity:Feedback|Feedback]], [[Adding a Activity:Quiz|Quiz]]. * [[Adding a Resource]] : [[Adding a Resource:Label|Label]], [[Adding a Resource:Files|Files]], [[Adding a Resource:External Tool|External Tool]], [[Adding a Resource:URL|URL]]. 491fbba003d661c7387069571cd1c15aac9b137b 77 74 2013-05-27T16:40:14Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Adicionando uma Atividade:Forum|Forum]], [[Adicionando uma Atividade:Chat|Chat]], [[Adicionando uma Atividade:Offline Activity|Offline Activity]], [[Adicionando uma Atividade:Assignment - Upload a single file|Assignment - Upload a single file]], [[Adicionando uma Atividade:Assignment|Assignment]], [[Adicionando uma Atividade:Feedback|Feedback]], [[Adicionando uma Atividade:Quiz|Quiz]]. * [[Adding a Resource]] : [[Adding a Resource:Label|Label]], [[Adding a Resource:Files|Files]], [[Adding a Resource:External Tool|External Tool]], [[Adding a Resource:URL|URL]]. 2ce3009411201a02d8c1fa1c74279c17fd97699c 85 77 2013-05-27T16:46:36Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Forum]], [[Chat]], [[Tarefa Offline]], [[Upload de arquivo único]], [[Tarefa]], [[Pesquisa]], [[Quiz]], [[Questionário]]. * [[Adicionando um Recurso]] : [[Rótulo]], [[Arquivos]], [[Ferramenta Externa (IMS LTI)]], [[URL]]. 004bff30534e28e78cb687e0f8cb4b46cbcf5dfa 86 85 2013-05-27T16:47:24Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Fórum]], [[Chat]], [[Tarefa Offline]], [[Upload de arquivo único]], [[Tarefa]], [[Pesquisa]], [[Quiz]], [[Questionário]]. * [[Adicionando um Recurso]] : [[Rótulo]], [[Arquivos]], [[Ferramenta Externa (IMS LTI)]], [[URL]]. ad4b4f608d7eef059d5ee6520dfaba94df0b7c05 87 86 2013-05-27T16:48:44Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Fórum]], [[Chat]], [[Atividade Offline]], [[Upload de arquivo único]], [[Tarefa]], [[Pesquisa]], [[Quiz]], [[Questionário]]. * [[Adicionando um Recurso]] : [[Rótulo]], [[Arquivos]], [[Ferramenta Externa (IMS LTI)]], [[URL]]. d5812243c67ed4b8b3ef9f3468397fb04c7d23ac 88 87 2013-05-27T16:49:28Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Fórum]], [[Chat]], [[Atividade Offline]], [[Envio de arquivo único]], [[Tarefa]], [[Pesquisa]], [[Questionário]]. * [[Adicionando um Recurso]] : [[Rótulo]], [[Arquivos]], [[Ferramenta Externa (IMS LTI)]], [[URL]]. 854a35d1a73f1574862490e3dc86e760bd098333 Moodle Documentation Index 0 39 67 2013-05-27T16:34:15Z Admin 1 Admin moved page [[Moodle Documentation Index]] to [[Moodle - Documentação]] wikitext text/x-wiki #REDIRECT [[Moodle - Documentação]] b403ce2d87e753783e5e7da8e80f2207cde1ef4c Criação de Curso 0 7 69 11 2013-05-27T16:35:27Z Admin 1 Admin moved page [[Creating Course]] to [[Criação de Curso]] wikitext text/x-wiki Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", clique no item "Cursos": aparecerão opções de configuração e de adição/modificação de cursos. Clique em "Acrescentar/modificar cursos". [[File:Criacao-de-curso_01.jpg|1040px]] Após o segundo clique, abrirá uma página de criação/edição de curso. Nesta página, os cursos são organizados por tipo, mostrados na tabela como "categorias de cursos". Mas como o objetivo é criar um curso, e não modificar um já existente, basta clicar no botão correspondente, que está fora da tabela. [[File:Criacao-de-curso_02.jpg|1040px]] Após clicar no botão para criar um curso, abrirá uma outra página, esta sendo para configuração do curso que será criado. Basicamente, só é preciso preencher os campos obrigatórios para criar, de fato, um curso. Esses campos obrigatórios são: o nome do curso (não existe um curso sem uma identificação), e um "nome breve do curso", que é nada mais do que uma abreviação, ou um código,para o nome do curso - como LIBRAS é uma abreviação para Língua Brasileira de Sinais. Apesar de só serem necessários os campos obrigatórios, é importante que se configure ao menos o formato do curso, que indica como o curso será organizado. No formato de tópicos, por exemplo, o curso será mostrado em, obviamente, tópicos, o que ajuda a ter um melhor controle de como o curso será organizado. Outro ponto importante é a configuração de grupos. A configuração controla a visibilidade dos grupos em relação aos outros grupos. Na "modalidade grupo", define-se como os grupos ficam visíveis uns aos outros: nenhum grupo - todos se vêem, grupos separados - o grupo vê somente as atividades de seu grupo, grupos visíveis - os membros de um grupo podem ver os outros grupos. No campo "forçar modalidade grupo", se a opção for sim, as atividades em grupos são forçadas a ter a mesma configuração de grupos do curso, e em caso contrário, as atividades podem ser configuradas de uma forma diferente do curso. Na configuração de disponibilidade, escolhemos se o curso aparece ou não na página de cursos. [[File:Criacao-de-curso_03.png|1040px]] Ao fim da configuração, basta clicar em "salvar mudanças", e então o curso será criado! :-) [[File:Criacao-de-curso_04.png|1040px]] b52abb125caa4ac5a677e01633f091dc806bb137 Creating Course 0 40 70 2013-05-27T16:35:27Z Admin 1 Admin moved page [[Creating Course]] to [[Criação de Curso]] wikitext text/x-wiki #REDIRECT [[Criação de Curso]] 7201c647c51a1d6c360600683c5cc429abe5d4d4 Adicionando uma Atividade 0 24 75 36 2013-05-27T16:38:33Z Admin 1 Admin moved page [[Adding a Activity]] to [[Adicionando uma Atividade]] wikitext text/x-wiki * [[Adding a Activity:Forum|Forum]] * [[Adding a Activity:Chat|Chat]] * [[Adding a Activity:Offline Activity|Offline Activity]] * [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]] * [[Adding a Activity:Assignment|Assignment]] * [[Adding a Activity:Feedback|Feedback]] * [[Adding a Activity:Quiz|Quiz]] 83fc55d5c3255a75944e1f37814be6ee890652b0 80 75 2013-05-27T16:41:20Z Admin 1 wikitext text/x-wiki * [[Adding a Activity:Forum|Forum]] * [[Adding a Activity:Chat|Chat]] * [[Atividade Offline]] * [[Adding a Activity:Assignment - Upload a single file|Assignment - Upload a single file]] * [[Adding a Activity:Assignment|Assignment]] * [[Adding a Activity:Feedback|Feedback]] * [[Adding a Activity:Quiz|Quiz]] b6e3d636270da0b5034f90ae32c0829330285c99 91 80 2013-05-27T17:26:57Z Admin 1 wikitext text/x-wiki * [[Fórum]] * [[Chat]] * [[Atividade Offline]] * [[Envio de arquivo único]] * [[Tarefa]] * [[Pesquisa]] * [[Questionário]] 5e9454c43bdd470d381811534004962cc2c4e829 Adding a Activity 0 41 76 2013-05-27T16:38:33Z Admin 1 Admin moved page [[Adding a Activity]] to [[Adicionando uma Atividade]] wikitext text/x-wiki #REDIRECT [[Adicionando uma Atividade]] 4c88f7e8b76fff742be40bea531f552d17632912 Adding a Activity:Offline Activity 0 42 79 2013-05-27T16:40:46Z Admin 1 Admin moved page [[Adding a Activity:Offline Activity]] to [[Atividade Offline]] wikitext text/x-wiki #REDIRECT [[Atividade Offline]] 35150f78ff6a72483db957bb7856026ecea83850 Adding a Activity:Chat 0 43 82 2013-05-27T16:42:40Z Admin 1 Admin moved page [[Adding a Activity:Chat]] to [[Chat]] wikitext text/x-wiki #REDIRECT [[Chat]] a0a295d9912131cfeee118544d07867d10436842 Fórum 0 26 83 50 2013-05-27T16:43:05Z Admin 1 Admin moved page [[Adding a Activity:Forum]] to [[Fórum]] wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Add an activity or resource", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. {| border="1" |[[File:adicao-de-atividade-forum.png|1040px]] |} Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). {| border="1" |[[File:adicao-de-atividade-forum_02.png|1040px]] |} Depois de configurar o fórum, salve-o. {| border="1" |[[File:adicao-de-atividade-forum_03.png|1040px]] |} 365c11672883ccf24329d0e2727456b2c68c366f Adding a Activity:Forum 0 44 84 2013-05-27T16:43:05Z Admin 1 Admin moved page [[Adding a Activity:Forum]] to [[Fórum]] wikitext text/x-wiki #REDIRECT [[Fórum]] 08c073812e14a8f4c60bc2ce7f021b3c76218b74 Main Page 0 45 90 2013-05-27T17:23:42Z Admin 1 Admin moved page [[Main Page]] to [[Logic Wiki (LoLITA)]] wikitext text/x-wiki #REDIRECT [[Logic Wiki (LoLITA)]] f3457e2b91a919ac48605d00a6d0514202ebf61b File:Adicao-de-atividades-envio-de-arquivo-unico 03.png 6 46 92 2013-05-27T17:28:56Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-envio-de-arquivo-unico 02.png 6 47 93 2013-05-27T17:28:58Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-envio-de-arquivo-unico 01.png 6 48 94 2013-05-27T17:29:00Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Envio de arquivo único 0 49 95 2013-05-27T17:29:38Z Admin 1 Created page with " Para adicionar atividades, é preciso que o modo de edição esteja ativo: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um envio de arquivo único, ..." wikitext text/x-wiki Para adicionar atividades, é preciso que o modo de edição esteja ativo: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um envio de arquivo único, clicamos em "Adicionar uma atividade ou recurso", e então selecionamos "Envio de arquivo único", nas opções de tarefas. {| border="1" |[[File:adicao-de-atividades-envio-de-arquivo-unico_01.png|1040px]] |} Como de costume, aparecerá uma página de configuração da tarefa. Os campos obrigatórios são somente o nome e a descrição da tarefa, mas é importante configurar também o período em que o envio estará disponível, se só pode haver um único envio, e como será dada a nota da atividade. {| border="1" |[[File:adicao-de-atividades-envio-de-arquivo-unico_02.png|1040px]] |} Depois de configurada a atividade, basta salvar. {| border="1" |[[File:adicao-de-atividades-envio-de-arquivo-unico_03.png|1040px]] |} ff9567c5f4b91c009f2930e45ae2915a7977bd0f 96 95 2013-05-27T17:30:04Z Admin 1 wikitext text/x-wiki Para adicionar atividades, é preciso que o modo de edição esteja ativo: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um envio de arquivo único, clicamos em "Adicionar uma atividade ou recurso", e então selecionamos "Envio de arquivo único", nas opções de tarefas. {| border="1" |[[File:adicao-de-atividades-envio-de-arquivo-unico_01.png|1040px]] |} Como de costume, aparecerá uma página de configuração da tarefa. Os campos obrigatórios são somente o nome e a descrição da tarefa, mas é importante configurar também o período em que o envio estará disponível, se só pode haver um único envio, e como será dada a nota da atividade. {| border="1" |[[File:adicao-de-atividades-envio-de-arquivo-unico_02.png|1040px]] |} Depois de configurada a atividade, basta salvar. {| border="1" |[[File:adicao-de-atividades-envio-de-arquivo-unico_03.png|1040px]] |} 8c3c77611c24ecb6b66d9b5ea0248f2c51f788cd Uma lista de Ferramentas para o Ensino de Lógica 0 50 97 2013-05-27T18:04:21Z Admin 1 Created page with " Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association ..." wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic e também a lista de Wiedijk2007} com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [[http://staff.science.uva.nl/~lhendrik/AkkaStart.html]] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [[http://www.cs.chalmers.se/~sydow/alfie/index.html]] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [[http://www.phil.cmu.edu/projects/apros/]] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [[http://www.asacalcpro.com.br/GiacomoN/index.htm Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [[http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html]] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [[http://dutiih.twi.tudelft.nl/~sicco/]] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [[http://logic.glashoff.net/aristotelianlogic/]] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [[http://pauillac.inria.fr/coq/]] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [[http://www.cc.utah.edu/~nahaj/logic/evaluate/]] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [[http://www2.sis.pitt.edu/~genie/]] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa aspectos de - Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; - Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; - Lógica Dinâmica: Calcula a transação de uma expressão regular dada; - Simulação Máquinas de Turing. [[http://staff.science.uva.nl/~jaspars/animations/]] Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [[http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html]] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [[http://www.logicinaction.org/]] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [[http://www.irit.fr/Lotrec/]] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [[https://github.com/rkirsling/modallogic]]. [[http://rkirsling.github.io/modallogic/]] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [[http://d3js.org/]]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [[http://www.mathjax.org/]]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [[http://twitter.github.io/bootstrap/]]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. [[http://ggww2.stanford.edu/GUS/lpl/]] Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [[http://www.doc.ic.ac.uk/pandora/]] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos. [[http://www.utexas.edu/courses/plato/]] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [[http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html]] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [[http://proofweb.cs.ru.nl/]] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [[http://selfpace.uconn.edu/BertieTwootie/software.htm]] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [[http://logic.tamu.edu/]] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. http://gtps.math.cmu.edu/tps.html]] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [[http://www.brian-borowski.com/Software/Truth/]] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. [[http://logic.philosophy.ox.ac.uk/tableau3/install.htm]] == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [[http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar]] DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [[http://www.ucalgary.ca- /aslcle/logic-courseware/]]. WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [[http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]]. 64d3bb339d864530db670a1892f576b7ee0df722 98 97 2013-05-27T18:04:46Z Admin 1 /* Alfie */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic e também a lista de Wiedijk2007} com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [[http://staff.science.uva.nl/~lhendrik/AkkaStart.html]] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [[http://www.cs.chalmers.se/~sydow/alfie/index.html]] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [[http://www.phil.cmu.edu/projects/apros/]] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [[http://www.asacalcpro.com.br/GiacomoN/index.htm Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [[http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html]] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [[http://dutiih.twi.tudelft.nl/~sicco/]] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [[http://logic.glashoff.net/aristotelianlogic/]] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [[http://pauillac.inria.fr/coq/]] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [[http://www.cc.utah.edu/~nahaj/logic/evaluate/]] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [[http://www2.sis.pitt.edu/~genie/]] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa aspectos de - Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; - Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; - Lógica Dinâmica: Calcula a transação de uma expressão regular dada; - Simulação Máquinas de Turing. [[http://staff.science.uva.nl/~jaspars/animations/]] Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [[http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html]] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [[http://www.logicinaction.org/]] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [[http://www.irit.fr/Lotrec/]] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [[https://github.com/rkirsling/modallogic]]. [[http://rkirsling.github.io/modallogic/]] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [[http://d3js.org/]]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [[http://www.mathjax.org/]]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [[http://twitter.github.io/bootstrap/]]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. [[http://ggww2.stanford.edu/GUS/lpl/]] Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [[http://www.doc.ic.ac.uk/pandora/]] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos. [[http://www.utexas.edu/courses/plato/]] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [[http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html]] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [[http://proofweb.cs.ru.nl/]] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [[http://selfpace.uconn.edu/BertieTwootie/software.htm]] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [[http://logic.tamu.edu/]] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. http://gtps.math.cmu.edu/tps.html]] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [[http://www.brian-borowski.com/Software/Truth/]] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. [[http://logic.philosophy.ox.ac.uk/tableau3/install.htm]] == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [[http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar]] DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [[http://www.ucalgary.ca- /aslcle/logic-courseware/]]. WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [[http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]]. fa6d08bc87f90fbfe238b6b714f6b1ea4b82327e 99 98 2013-05-27T18:05:01Z Admin 1 /* ASA-CalcPro */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic e também a lista de Wiedijk2007} com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [[http://staff.science.uva.nl/~lhendrik/AkkaStart.html]] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [[http://www.cs.chalmers.se/~sydow/alfie/index.html]] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [[http://www.phil.cmu.edu/projects/apros/]] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [[http://www.asacalcpro.com.br/GiacomoN/index.htm]] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [[http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html]] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [[http://dutiih.twi.tudelft.nl/~sicco/]] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [[http://logic.glashoff.net/aristotelianlogic/]] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [[http://pauillac.inria.fr/coq/]] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [[http://www.cc.utah.edu/~nahaj/logic/evaluate/]] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [[http://www2.sis.pitt.edu/~genie/]] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa aspectos de - Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; - Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; - Lógica Dinâmica: Calcula a transação de uma expressão regular dada; - Simulação Máquinas de Turing. [[http://staff.science.uva.nl/~jaspars/animations/]] Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [[http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html]] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [[http://www.logicinaction.org/]] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [[http://www.irit.fr/Lotrec/]] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [[https://github.com/rkirsling/modallogic]]. [[http://rkirsling.github.io/modallogic/]] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [[http://d3js.org/]]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [[http://www.mathjax.org/]]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [[http://twitter.github.io/bootstrap/]]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. [[http://ggww2.stanford.edu/GUS/lpl/]] Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [[http://www.doc.ic.ac.uk/pandora/]] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos. [[http://www.utexas.edu/courses/plato/]] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [[http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html]] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [[http://proofweb.cs.ru.nl/]] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [[http://selfpace.uconn.edu/BertieTwootie/software.htm]] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [[http://logic.tamu.edu/]] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. http://gtps.math.cmu.edu/tps.html]] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [[http://www.brian-borowski.com/Software/Truth/]] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. [[http://logic.philosophy.ox.ac.uk/tableau3/install.htm]] == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [[http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar]] DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [[http://www.ucalgary.ca- /aslcle/logic-courseware/]]. WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [[http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]]. c1ea98962ed1611154189a483301b1de07371d47 100 99 2013-05-27T18:08:20Z Admin 1 wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa aspectos de - Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; - Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; - Lógica Dinâmica: Calcula a transação de uma expressão regular dada; - Simulação Máquinas de Turing. [http://staff.science.uva.nl/~jaspars/animations/] Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. [http://ggww2.stanford.edu/GUS/lpl/] Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos. [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> </references> adf3008cfd61de79705a00e9d12f5ebdee15623f 101 100 2013-05-27T18:08:47Z Admin 1 /* LoTREC */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa aspectos de - Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; - Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; - Lógica Dinâmica: Calcula a transação de uma expressão regular dada; - Simulação Máquinas de Turing. [http://staff.science.uva.nl/~jaspars/animations/] Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. [http://ggww2.stanford.edu/GUS/lpl/] Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos. [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> </references> f8d6ffca8ea1949624d056b7d73e8701ea055f01 102 101 2013-05-27T18:13:51Z Admin 1 wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> </references> c99828bb2b7c5d107152b8386a739b3db0946014 103 102 2013-05-27T18:14:24Z Admin 1 /* Plato */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> </references> 96fd6801fca6444563358af0ff22a8f986f8a184 File:Adicao-de-atividades-pesquisa 03.png 6 51 104 2013-05-27T18:19:47Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-pesquisa 02.png 6 52 105 2013-05-27T18:19:48Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-pesquisa 01.png 6 53 106 2013-05-27T18:19:48Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Pesquisa 0 54 107 2013-05-27T18:22:26Z Admin 1 Created page with " '''Uma Pesquisa pode ser realizada, por exemplo, para consultar os participantes sobre um horário de disponibilidade para a realização de um chat ou horários de monitoria..." wikitext text/x-wiki '''Uma Pesquisa pode ser realizada, por exemplo, para consultar os participantes sobre um horário de disponibilidade para a realização de um chat ou horários de monitoria.''' {| border="1" |[[File:adicao-de-atividades-pesquisa_01.png|1040px]] |} Para adicionar uma pesquisa, o procedimento é um pouco diferente depois da configuração: é necessário inserir as questões da pesquisa. {| border="1" |[[File:adicao-de-atividades-pesquisa_02.png|1040px]] |} As questões devem ser inseridas de acordo com o tipo de resposta que ela deve ter. Cada página de configuração de questão é diferente. Mas, basicamente, as configurações de questões de múltipla escolha se assemelham, assim como questões de resposta textual. {| border="1" |[[File:adicao-de-atividades-pesquisa_03.png|1040px]] |} 1e0d4aebd0cdebaf48f289d553c699b7e8cf6b5c Pesquisa 0 54 108 107 2013-05-27T18:25:12Z Admin 1 wikitext text/x-wiki '''Uma Pesquisa pode ser realizada, por exemplo, para consultar os participantes sobre um horário de disponibilidade para a realização de um chat ou horários de monitoria.''' {| border="1" |[[File:adicao-de-atividades-pesquisa_01.png|1040px]] |} Para adicionar uma pesquisa, o procedimento é um pouco diferente depois da configuração: é necessário inserir as questões da pesquisa. {| border="1" |[[File:adicao-de-atividades-pesquisa_02.png|1040px]] |} As questões devem ser inseridas de acordo com o tipo de resposta que ela deve ter. {| border="1" |[[File:adicao-de-atividades-pesquisa_03.png|1040px]] |} Cada página de configuração de questão é diferente. Mas, basicamente, as configurações de questões de múltipla escolha se assemelham, assim como questões de resposta textual. {| border="1" |[[File:adicao-de-atividades-pesquisa_04.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-pesquisa_05.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-pesquisa_08.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-pesquisa_09.png|1040px]] |} 40e22575dc9cb982d82185faeafccd80dc091ee5 File:Adicao-de-atividades-pesquisa 09.png 6 55 109 2013-05-27T18:25:22Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-pesquisa 08.png 6 56 110 2013-05-27T18:25:23Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-pesquisa 05.png 6 57 111 2013-05-27T18:25:23Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-pesquisa 04.png 6 58 112 2013-05-27T18:25:24Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 07.png 6 59 113 2013-05-27T18:28:20Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 06.png 6 60 114 2013-05-27T18:28:21Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 05.png 6 61 115 2013-05-27T18:28:21Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 04.png 6 62 116 2013-05-27T18:28:22Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 03.png 6 63 117 2013-05-27T18:28:22Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 02.png 6 64 118 2013-05-27T18:28:23Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Adicao-de-atividades-questionario 01.png 6 65 119 2013-05-27T18:28:23Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Questionário 0 66 120 2013-05-27T18:41:01Z Admin 1 Created page with " Em poucas palavras, um questionário funciona como uma pesquisa, mas com correção automática e outros tipos possíveis de resposta. Para adicionar um questionário, c..." wikitext text/x-wiki Em poucas palavras, um questionário funciona como uma pesquisa, mas com correção automática e outros tipos possíveis de resposta. Para adicionar um questionário, clicamos em "Add an activity or resource", e então selecionamos a opção "Questionário". {| border="1" |[[:File:adicao-de-atividades-questionario_01.png| 1040px]] |} Aparecerá uma página de configuração do questionário. Depois de configurado, inserimos as questões do questionário. {| border="1" |[[:File:adicao-de-atividades-questionario_02.png| 1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_03.png| 1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_04.png| 1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_05.png| 1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_06.png| 1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_07.png| 1040px]] |} fc30c7b734a9e61969f5f576b61e42673980dc23 121 120 2013-05-27T18:41:36Z Admin 1 wikitext text/x-wiki Em poucas palavras, um questionário funciona como uma pesquisa, mas com correção automática e outros tipos possíveis de resposta. Para adicionar um questionário, clicamos em "Add an activity or resource", e então selecionamos a opção "Questionário". {| border="1" |[[:File:adicao-de-atividades-questionario_01.png|1040px]] |} Aparecerá uma página de configuração do questionário. Depois de configurado, inserimos as questões do questionário. {| border="1" |[[:File:adicao-de-atividades-questionario_02.png|1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_03.png|1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_04.png|1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_05.png|1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_06.png|1040px]] |} {| border="1" |[[:File:adicao-de-atividades-questionario_07.png|1040px]] |} 89f7d010f14b7bd456eff89a4cc7f45b5368d2af 122 121 2013-05-27T18:43:01Z Admin 1 wikitext text/x-wiki Em poucas palavras, um questionário funciona como uma pesquisa, mas com correção automática e outros tipos possíveis de resposta. Para adicionar um questionário, clicamos em "Add an activity or resource", e então selecionamos a opção "Questionário". {| border="1" |[[File:adicao-de-atividades-questionario_01.png|1040px]] |} Aparecerá uma página de configuração do questionário. Depois de configurado, inserimos as questões do questionário. {| border="1" |[[File:adicao-de-atividades-questionario_02.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-questionario_03.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-questionario_04.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-questionario_05.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-questionario_06.png|1040px]] |} {| border="1" |[[File:adicao-de-atividades-questionario_07.png|1040px]] |} 3b97dd249cacb9370a0c900d73ee7530acf55c9e Moodle - Documentação 0 2 123 88 2013-05-27T18:44:31Z Admin 1 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Fórum]], [[Chat]], [[Atividade Offline]], [[Envio de arquivo único]], [[Pesquisa]], [[Questionário]]. * [[Adicionando um Recurso]] : [[Rótulo]], [[Arquivos]], [[Ferramenta Externa (IMS LTI)]], [[URL]]. b0c058fb19b335fed5a8e4a5ff7cb5b87e2759a4 151 123 2014-10-17T23:37:14Z Patrickts 2 wikitext text/x-wiki Setting and Course Administration * [[Criação de Curso]] * [[Cadastro de um Participante em um Curso]] * [[Grupos e Agrupamentos]] * [[Adicionando uma Atividade]] : [[Fórum]], [[Chat]], [[Atividade Offline]], [[Envio de arquivo único]], [[Pesquisa]], [[Questionário]]. * [[Adicionando um Recurso]] : [[Rótulo]], [[Arquivos]], [[Ferramenta Externa (IMS LTI)]], [[URL]]. * Adicionando um Recurso : [[TryLogic no Moodle]] b51e46009079bd801c41028f382fd42613f82ba8 Fórum 0 26 124 83 2013-05-27T18:45:02Z Admin 1 wikitext text/x-wiki Da mesma forma de como se adicionam recursos, para adicionar atividades é preciso primeiro ativar o modo de edição. {| border="1" |[[File:ativar_edicao.png|1040px]] |} Para adicionar um fórum, da mesma forma como para adicionar algum recurso, clicamos em "Adicionar uma atividade ou recurso", no canto inferior direito do tópico. Abrirá, então, um menu de opções. Selecionamos a opção "Fórum", na seção de Atividades. {| border="1" |[[File:adicao-de-atividade-forum.png|1040px]] |} Então, abrirá uma página de configuração do fórum. Apenas um nome e uma introdução são necessárias para que o fórum seja criado, mas há algumas configurações não obrigatórias importantes (estão descritas nas imagens). {| border="1" |[[File:adicao-de-atividade-forum_02.png|1040px]] |} Depois de configurar o fórum, salve-o. {| border="1" |[[File:adicao-de-atividade-forum_03.png|1040px]] |} 500756e46040f5222a6c9ee561771d5799b361d5 Chat 0 33 125 81 2013-05-27T18:45:17Z Admin 1 wikitext text/x-wiki Antes de tudo é preciso primeiro ativar o modo de edição: {| border="1" |[[File:ativar_edicao.png|1040px]] |} Da mesma forma como no fórum, para adicionar um chat clicamos em "Adicionar uma atividade ou recurso": e então selecionamos a opção "Chat". e salvar o chat. {| border="1" |[[File:adicao-de-atividades-chat_01.png|1040px]] |} Então, basta '''configurar''': {| border="1" |[[File:adicao-de-atividades-chat_02.png|1040px]] |} E salvar em seguida: {| border="1" |[[File:adicao-de-atividades-chat_03.png|1040px]] |} 84beff464210afccaf3323be5b3196f0a074b2e8 Atividade Offline 0 37 126 78 2013-05-27T18:45:34Z Admin 1 wikitext text/x-wiki Do mesmo modo como fizemos com as outras atividades, clicamos em "Adicionar uma atividade ou recurso", e então selecionamos a opção "Atividade Offline". [[File:adicao-de-atividades-atividades-offline_01.png|1040px]] Depois, aparecerá uma página de configuração da atividade. Basicamente, precisamos de um nome e uma descrição para a tarefa. [[File:adicao-de-atividades-atividades-offline_02.png|1040px]] Então, configuramos o período visualizável da tarefa e como será dada a nota da tarefa. Além disso, há a configuração dos grupos, que funciona da mesma forma como na configuração das outras atividades. [[File:adicao-de-atividades-atividades-offline_03.png|1040px]] a57509b2cc5b2fc751f1ff4298c7fc6feb2515e1 Ferramenta Externa (IMS LTI) 0 67 127 2013-05-27T18:51:51Z Admin 1 Created page with " O processo de adição de uma ferramenta externa começa pela ativação do modo de edição - clique no botão superior direito da tela. Com o modo de edição ativo, clic..." wikitext text/x-wiki O processo de adição de uma ferramenta externa começa pela ativação do modo de edição - clique no botão superior direito da tela. Com o modo de edição ativo, clicamos em "Adicionar uma atividade ou recurso", selecionamos a opção "Ferramenta Externa" e então clicamos em "Acrescentar". Depois disso, aparece uma página de configuração da ferramenta. Primeiro, clique em "Mostrar avançado", sob o menu "Geral" e somente então configure a ferramenta. Na configuração avançada, temos alguns aspectos importantes a serem considerados: * 1. '''Nome''': toda atividade precisa de um nome de identificação. * 2. '''Lançamento de URL''': precisamos de um endereço para poder carregar a ferramenta. * 3. '''Lançamento do Container''': no campo "Launch container" é que se define como a ferramenta aparece quando se clica no link da atividade. * 4. '''Chave do consumidor (''Consumer Key'')''': funciona como um login comum do Moodle para acesso à ferramenta. Sendo assim, é necessário definir a consumer key do Moodle para a ferramenta, se esta não estiver pré-configurada. * 5. '''Segredo compartilhado (''Shared Secret'')''': assim como a consumer key funciona como um login, o Shared Secret funciona como uma senha comum do Moodle para acesso à ferramenta. Se a ferramenta não for pré-configurada de forma que ela autentique requisições do Moodle automaticamente, é necessário informar o shared secret. Depois de configurar a ferramenta, basta mandar salvar. d610f634931b6dd97e91fa8ed37b236e6e6f1bce 145 127 2013-06-01T20:23:41Z Admin 1 wikitext text/x-wiki O processo de adição de uma ferramenta externa começa pela ativação do modo de edição - clique no botão superior direito da tela. [[File:ferramenta-externa-1.png|400px]] Com o modo de edição ativo, clicamos em "Adicionar uma atividade ou recurso", selecionamos a opção "Ferramenta Externa" e então clicamos em "Acrescentar". Depois disso, aparece uma página de configuração da ferramenta. Primeiro, clique em "Mostrar avançado", sob o menu "Geral" e somente então configure a ferramenta. [[File:ferramenta-externa-2.png|400px]] Na configuração avançada, temos alguns aspectos importantes a serem considerados: * 1. '''Nome''': toda atividade precisa de um nome de identificação. * 2. '''Lançamento de URL''': precisamos de um endereço para poder carregar a ferramenta. * 3. '''Lançamento do Container''': no campo "Launch container" é que se define como a ferramenta aparece quando se clica no link da atividade. * 4. '''Chave do consumidor (''Consumer Key'')''': funciona como um login comum do Moodle para acesso à ferramenta. Sendo assim, é necessário definir a consumer key do Moodle para a ferramenta, se esta não estiver pré-configurada. * 5. '''Segredo compartilhado (''Shared Secret'')''': assim como a consumer key funciona como um login, o Shared Secret funciona como uma senha comum do Moodle para acesso à ferramenta. Se a ferramenta não for pré-configurada de forma que ela autentique requisições do Moodle automaticamente, é necessário informar o shared secret. [[File:ferramenta-externa-3.png|400px]] Depois de configurar a ferramenta, basta mandar salvar. [[File:ferramenta-externa-4.png|400px]] b6b9cf88b7dcbdc8b668d85ac00d2f5c88e91333 146 145 2013-06-01T20:25:37Z Admin 1 wikitext text/x-wiki O processo de adição de uma ferramenta externa começa pela ativação do modo de edição - clique no botão superior direito da tela. {| border="1" |[[File:ferramenta-externa-1.png|1040px]] } Com o modo de edição ativo, clicamos em "Adicionar uma atividade ou recurso", selecionamos a opção "Ferramenta Externa" e então clicamos em "Acrescentar". Depois disso, aparece uma página de configuração da ferramenta. Primeiro, clique em "Mostrar avançado", sob o menu "Geral" e somente então configure a ferramenta. {| border="1" |[[File:ferramenta-externa-2.png|1040px]] } Na configuração avançada, temos alguns aspectos importantes a serem considerados: * 1. '''Nome''': toda atividade precisa de um nome de identificação. * 2. '''Lançamento de URL''': precisamos de um endereço para poder carregar a ferramenta. * 3. '''Lançamento do Container''': no campo "Launch container" é que se define como a ferramenta aparece quando se clica no link da atividade. * 4. '''Chave do consumidor (''Consumer Key'')''': funciona como um login comum do Moodle para acesso à ferramenta. Sendo assim, é necessário definir a consumer key do Moodle para a ferramenta, se esta não estiver pré-configurada. * 5. '''Segredo compartilhado (''Shared Secret'')''': assim como a consumer key funciona como um login, o Shared Secret funciona como uma senha comum do Moodle para acesso à ferramenta. Se a ferramenta não for pré-configurada de forma que ela autentique requisições do Moodle automaticamente, é necessário informar o shared secret. {| border="1" |[[File:ferramenta-externa-3.png|1040px]] } Depois de configurar a ferramenta, basta mandar salvar. {| border="1" |[[File:ferramenta-externa-4.png|1040px]] } 762dbb757c3e27320fd022d48d3671e2a80c5fe7 147 146 2013-06-01T20:26:18Z Admin 1 wikitext text/x-wiki O processo de adição de uma ferramenta externa começa pela ativação do modo de edição - clique no botão superior direito da tela. {| border="1" |[[File:ferramenta-externa-1.png|1040px]] |} Com o modo de edição ativo, clicamos em "Adicionar uma atividade ou recurso", selecionamos a opção "Ferramenta Externa" e então clicamos em "Acrescentar". Depois disso, aparece uma página de configuração da ferramenta. Primeiro, clique em "Mostrar avançado", sob o menu "Geral" e somente então configure a ferramenta. {| border="1" |[[File:ferramenta-externa-2.png|1040px]] |} Na configuração avançada, temos alguns aspectos importantes a serem considerados: * 1. '''Nome''': toda atividade precisa de um nome de identificação. * 2. '''Lançamento de URL''': precisamos de um endereço para poder carregar a ferramenta. * 3. '''Lançamento do Container''': no campo "Launch container" é que se define como a ferramenta aparece quando se clica no link da atividade. * 4. '''Chave do consumidor (''Consumer Key'')''': funciona como um login comum do Moodle para acesso à ferramenta. Sendo assim, é necessário definir a consumer key do Moodle para a ferramenta, se esta não estiver pré-configurada. * 5. '''Segredo compartilhado (''Shared Secret'')''': assim como a consumer key funciona como um login, o Shared Secret funciona como uma senha comum do Moodle para acesso à ferramenta. Se a ferramenta não for pré-configurada de forma que ela autentique requisições do Moodle automaticamente, é necessário informar o shared secret. {| border="1" |[[File:ferramenta-externa-3.png|1040px]] |} Depois de configurar a ferramenta, basta mandar salvar. {| border="1" |[[File:ferramenta-externa-4.png|1040px]] |} e7cc0c370841145bbb5d08575dd7dd9663dd4d4e 148 147 2013-06-01T20:28:37Z Admin 1 wikitext text/x-wiki O processo de adição de uma ferramenta externa começa pela ativação do modo de edição - clique no botão superior direito da tela. {| border="1" |[[File:ferramenta-externa-1.png|640px]] |} Com o modo de edição ativo, clicamos em "Adicionar uma atividade ou recurso", selecionamos a opção "Ferramenta Externa" e então clicamos em "Acrescentar". Depois disso, aparece uma página de configuração da ferramenta. Primeiro, clique em "Mostrar avançado", sob o menu "Geral" e somente então configure a ferramenta. {| border="1" |[[File:ferramenta-externa-2.png|640px]] |} Na configuração avançada, temos alguns aspectos importantes a serem considerados: * 1. '''Nome''': toda atividade precisa de um nome de identificação. * 2. '''Lançamento de URL''': precisamos de um endereço para poder carregar a ferramenta. * 3. '''Lançamento do Container''': no campo "Launch container" é que se define como a ferramenta aparece quando se clica no link da atividade. * 4. '''Chave do consumidor (''Consumer Key'')''': funciona como um login comum do Moodle para acesso à ferramenta. Sendo assim, é necessário definir a consumer key do Moodle para a ferramenta, se esta não estiver pré-configurada. * 5. '''Segredo compartilhado (''Shared Secret'')''': assim como a consumer key funciona como um login, o Shared Secret funciona como uma senha comum do Moodle para acesso à ferramenta. Se a ferramenta não for pré-configurada de forma que ela autentique requisições do Moodle automaticamente, é necessário informar o shared secret. {| border="1" |[[File:ferramenta-externa-3.png|640px]] |} Depois de configurar a ferramenta, basta mandar salvar. {| border="1" |[[File:ferramenta-externa-4.png|640px]] |} 02254ce4ffb43d3ef997c3b8e8488e037fb5d517 Uma lista de Ferramentas para o Ensino de Lógica 0 50 128 103 2013-05-27T18:53:53Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> </references> fe953c3fa6c9558a46a7f104de2768eb5859721d 129 128 2013-05-27T18:54:13Z Admin 1 wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> </references> 79a464c1ab6eb3a8e33bac06a3f5ba8749401cd5 130 129 2013-05-27T20:09:56Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== {{reflist|refs= <ref name="asl"> DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/]. </ref> <ref name="wiedijk"> WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimath/bycategory.html]. </ref> }} d448ba701d1a299d46e509dadc0f926af1434420 131 130 2013-05-27T20:10:48Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl">DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/].</ref> <ref name="wiedijk">WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/ ̃freek/digimathbycategory.html].</ref> </references> 0b2efb4695bc92dff9d3a3efba729a765bf78eaa 132 131 2013-05-27T21:12:43Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl">DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca-/aslcle/logic-courseware/].</ref> <ref name="wiedijk">WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/freek/digimathbycategory.html].</ref> </references> 88c052cab001c7fdc34caa38e2715eae5139c0f3 133 132 2013-05-27T21:13:10Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl">DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca/aslcle/logic-courseware/].</ref> <ref name="wiedijk">WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/freek/digimathbycategory.html].</ref> </references> b7a365ef6145052e68fdc5207807fc0614eba23b 134 133 2013-05-27T21:13:43Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl">DITMARSCH, H. van. Logic courseware. 2010. '''A Comprehensive list of Tools for doing Logic'''. Association for Symbolic Logic. Disponível em: </ref> <ref name="wiedijk">WIEDIJK, F. '''Digital Math by Categories with Samples'''. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: .</ref> </references> [http://www.ucalgary.ca/aslcle/logic-courseware/]. [http://www.cs.ru.nl/freek/digimathbycategory.html] d6556c9637e7813d410d5aafda9090684ba7fafd 135 134 2013-05-28T00:04:00Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name='asl'>DITMARSCH, H. van. Logic courseware. 2010. A Comprehensive list of Tools for doing Logic. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca/aslcle/logic-courseware/]. </ref> <ref name='wiedijk'>WIEDIJK, F. Digital Math by Categories with Samples. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/freek/digimathbycategory.html].</ref> </references> e2c301fb604d1b4d8be9ee1a7bf356819d596e16 136 135 2013-05-28T00:04:21Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name="asl">DITMARSCH, H. van. Logic courseware. 2010. A Comprehensive list of Tools for doing Logic. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca/aslcle/logic-courseware/]. </ref> <ref name="wiedijk">WIEDIJK, F. Digital Math by Categories with Samples. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/freek/digimathbycategory.html].</ref> </references> dd9f974827230ac2158bbb18595c6929ae0c8c0e 137 136 2013-05-28T00:05:17Z Admin 1 /* References */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref name="d3"/>, MathJax<ref name="mj"/> e o Twitter Bootstrap<ref name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name=asl>DITMARSCH, H. van. Logic courseware. 2010. A Comprehensive list of Tools for doing Logic. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca/aslcle/logic-courseware/]. </ref> <ref name=wiedijk>WIEDIJK, F. Digital Math by Categories with Samples. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/freek/digimathbycategory.html].</ref> </references> 8f4e9ea53d597833c56cab18a870ca41541efc04 138 137 2013-05-28T00:18:15Z Admin 1 /* Modal Logic Playground */ wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref group="modal" name="d3"/>, MathJax<ref group="modal" name="mj"/> e o Twitter Bootstrap<ref group="modal" name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references group="modal"> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name=asl>DITMARSCH, H. van. Logic courseware. 2010. A Comprehensive list of Tools for doing Logic. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca/aslcle/logic-courseware/]. </ref> <ref name=wiedijk>WIEDIJK, F. Digital Math by Categories with Samples. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/freek/digimathbycategory.html].</ref> </references> 131dc6fe1ae8d6dc652f30326d3a1af12113a38a 139 138 2013-05-28T00:20:55Z Admin 1 wikitext text/x-wiki Listamos aqui uma seleção de softwares para o Ensino de Lógica com suas respectivas descrições. Além de nossa lista, há uma lista mantida pelo grupo da Association for Symbolic Logic<ref name="asl"/> e também a lista de Freek Wiedijk<ref name="wiedijk"/> com 300 ferramentas computacionais de aplicações e ensino de Lógica. == Akka == Criador e Editor de Modelos de Kripke. Foi desenvolvido em Java. [http://staff.science.uva.nl/~lhendrik/AkkaStart.html] Avaliação: - Última atualização: - Problemas: - == Alfie == É um programa editor de demonstrações em lógica proposicional. Tem a vantagem de apresentar a demonstração tal como um programa em Haskell. [http://www.cs.chalmers.se/~sydow/alfie/index.html] Avaliação: - Última atualização: -. Problemas: - == AproS == Um projeto que consiste em quatro sistemas integrados um mecanismo de busca de demonstrações Proof Generator, Proof Tutor, Proof Lab e curso web Logic & Proofs. É desenvolvido em Java Web Start. [http://www.phil.cmu.edu/projects/apros/] Avaliação: - Última atualização: - Problemas: - == ASA-CalcPro == É um ambiente de suporte ao aluno para o ensino-aprendizagem de cálculo proposicional. [http://www.asacalcpro.com.br/GiacomoN/index.htm] Avaliação: - Última atualização: - Problemas: - == Bertrand == Symbolic Logic Problem-Solving Software: Resolve conjuntos proposições de primeira ordem no que diz respeito a consistência, validade e equivalência. Classifica verdades lógicas e falsidades lógicas, e constrói tabelas de verdade. [http://www.uwosh.edu/faculty_staff/herzberg/Bertrand.html] Avaliação: - Última atualização: - Problemas: - == BOP == Ferramenta educativa desenvolvida em Java para demonstrações à la Fitch da Delft university. [http://dutiih.twi.tudelft.nl/~sicco/] Avaliação: - Última atualização: - Problemas: - == Computational Aristotelian Term Logic == Site dedicado aos aspectos formais da lógica aristotélica tradicional. Desenvolvido em PHP. [http://logic.glashoff.net/aristotelianlogic/] Avaliação: - Última atualização: - Problemas: - == Coq == É um assistente de demonstrações formais, permite definir funções ou predicados, estabelecer teoremas matemáticos e especificações de softwares, desenvolver demonstrações formais interativamente e verificar estas demonstrações a partir de uma teoria formal. [http://pauillac.inria.fr/coq/] Avaliação: - Última atualização: - Problemas: - == Expression Evaluator Page == Analisador de expressões booleanas, com quantificadores e predicados. É um programa em CGI escrito em Perl. [http://www.cc.utah.edu/~nahaj/logic/evaluate/] Avaliação: - Última atualização: - Problemas: - == Genie == É um ambiente de desenvolvimento para construção de modelos gráficos de decisões teóricas. [http://www2.sis.pitt.edu/~genie/] Avaliação: - Última atualização: 1999. Problemas: Desatualizado. == Introdutory Logic Animations == Iniciativa idealizada e desenvolvida em 1999 por Jan Jaspars, contém programas em JavaScript para aprendizado interativo de lógica matemática básica. Implementa [http://staff.science.uva.nl/~jaspars/animations/] aspectos como * Lógica proposicional e de primeira ordem: constrói modelos, contra-modelos, tabelas de verdade, avalia fórmulas e encontra instâncias para variáveis livres; * Lógica Modal: Calcula mundos dado um modelo e constrói mundos possíveis; * Lógica Dinâmica: Calcula a transação de uma expressão regular dada; * Simulação Máquinas de Turing. Avaliação:[Muito bom] Última atualização: 1999. Problemas: Desatualizado. == Jape - Just Another Proof Editor == É um sistema desktop para prática de demonstrações de dedução natural. Bastante interessante e flexível que possibilita definir novas formalizações lógicas. [http://www.cs.ox.ac.uk/people/bernard.sufrin/jape.html] Avaliação:[Muito bom] Última atualização: 2012. Problemas: - == Logic in Action == Trata-se da continuidade da iniciativa do projeto \ep{Introdutory Logic Animations de Jan Jaspars para o ensino de Lógica em cursos introdutórios e intermediários. O projeto disponibiliza materiais de aula, slides em inglês e espanhol, e programas interativos para ensinar Lógica Proposicional, Raciocínio Silogístico, Lógica de Primeira Ordem, Lógica Epistêmica, Lógica Dinâmica, Jogos Lógicos, Testes de Validade, Demonstrações e Método de Resolução. [http://www.logicinaction.org/] Avaliação:[Muito bom] Última atualização: 2013. Problemas: Programas não funcionam, estão com endereços quebrados. == LoTREC == Trata-se de uma ferramenta para a demonstração através do método de Tableau para Lógica de $1ª$ Ordem e Lógica Modal, implementada em Java e executável via Java Web Start. [http://www.irit.fr/Lotrec/] Avaliação: [Muito bom] Última atualização: 2013. Problemas: - == Modal Logic Playground == Trata-se de um avaliador de fórmulas e de relações de acessibilidade para a Lógica Modal Proposicional. Foi desenvolvido através da biblioteca JavaScript D3<ref group="modal" name="d3"/>, MathJax<ref group="modal" name="mj"/> e o Twitter Bootstrap<ref group="modal" name="tb"/>. A ferramenta possui código livre disponível em [https://github.com/rkirsling/modallogic]. [http://rkirsling.github.io/modallogic/] Avaliação:[Muito bom] Última atualização: 2013 <references group="modal"> <ref name="d3"> Uma biblioteca JavaScript para manipulação de documentos a partir de seus dados. Disponível em [http://d3js.org/]. </ref> <ref name="mj"> Uma biblioteca JavaScript para visualização de notações matemáticas. Disponível em [http://www.mathjax.org/]. </ref> <ref name="tb"> Uma framework para desenvolvimento de interfaces web. Disponível em [http://twitter.github.io/bootstrap/]. </ref> </references> == Tarski's World, Fitch & Boole == São disponibilizados junto com o livro ''Language, Proof and Logic'', compõem três programas desktop que implementam diferentes aspectos da lógica, a saber, Modelos Semânticos, demonstrações formais \ep{à la Fitch e Tabelas de Verdade. [http://ggww2.stanford.edu/GUS/lpl/] * '''Tarski's World''': ensina as bases da linguagem de primeira ordem e sua semântica; * '''Fitch''': um ambiente de assistência e verificação de dedução natural. * '''Boole''': um programa que facilita a construção e verificação de tabelas de verdade relacionando com suas principais noções de tautologia, consequência e equivalência. Avaliação: - Última atualização: - Problemas: - == Pandora == (Proof Assistant for Natural Deduction using Organised Rectangular Areas): é uma ferramenta de aprendizado e suporte para a construção de demonstrações em dedução natural. Está disponível através de Java Applet's. [http://www.doc.ic.ac.uk/pandora/] Avaliação: - Última atualização: - Problemas: - == Plato == É uma ferramenta para a construção de demonstrações em linguagens formais de cálculo sentencial, cálculo de predicados e teoria dos conjuntos [http://www.utexas.edu/courses/plato/] Avaliação: - Última atualização: - Problemas: - == Possible World Creation == Analisa fórmulas modais a partir de um modelo de Kripke construído pelo usuário. [http://staff.science.uva.nl/~jaspars/lvi98/Week3/modal.html] == ProofWeb == É um sistema tanto para ensino de lógica e assistência de demonstrações na Web. É desenvolvido em PHP e serve como comunicação entre uma interface Web e o demonstrador de Teoremas Coq que funciona no servidor. [http://proofweb.cs.ru.nl/] Avaliação: - Última atualização: - Problemas: - == The Bertie & Twootie == É um verificador de demonstrações útil para busca de estratégias de demonstrações. E um verificador da correção de tableaux semântico. [http://selfpace.uconn.edu/BertieTwootie/software.htm] Avaliação: - Última atualização: - Problemas: - == The Daemon Proof Checker == Um verificador de demonstrações e de contramodelos. [http://logic.tamu.edu/] Avaliação: - Última atualização: - Problemas: - == Theorem Proving System == É um sistema educacional demonstrador de teoremas desenvolvido em Common Lisp. [http://gtps.math.cmu.edu/tps.html] Avaliação: - Última atualização: - Problemas: - == Truth Table Constructor == É um aplicação que constroí tabelas de verdade para lógica proposicional. Desenvolvido em Java Applet's. [http://www.brian-borowski.com/Software/Truth/] == Tableau3 == Construtor de Tableaux desenvolvido em Java Applet's. [http://logic.philosophy.ox.ac.uk/tableau3/install.htm] Avaliação:[Regular] Última atualização: 2005. Problemas: Desatualizados. Aplicações Desktop. == Summa Logicae XXI == Coletânea de diversos softwares relacionados com o ensino de lógica desenvolvidos em Java: DiagVenn1.0: Diagramas para lógica de predicados monádicos. MAFIA: Constrói tableaux proposicionais. Modelos de Kripke: Analisa fórmulas modais em modelos de Kripke definidos pelo usuário e verifica as propriedades de acessabilidade.Diagramas de Peirce: Permite a realização de um sistema para representação de gráficos. Possibilita o desenvolvimento em um sistema de gráficos de Peirce. Tradutor de lógicas: Ferramenta que proporciona um simulador gráfico de um tradutor de fórmulas em lógica modal para lógicas multivaloradas. [http://logicae.usal.es/mambo/index.php?option=com_summalogicaexxi&menu_task=Software&task=no_task&cmd=listar] ==References== <references> <ref name=asl>DITMARSCH, H. van. Logic courseware. 2010. A Comprehensive list of Tools for doing Logic. Association for Symbolic Logic. Disponível em: [http://www.ucalgary.ca/aslcle/logic-courseware/]. </ref> <ref name=wiedijk>WIEDIJK, F. Digital Math by Categories with Samples. 2007. A compiled list of 300 Tools sorted by implementation, interaction, supported logic and size effort. Disponível em: [http://www.cs.ru.nl/~freek/digimath/bycategory.html].</ref> </references> f7473af3c9c2bb3e84cac2946d2681744661434d File:Ferramenta-externa-4.png 6 68 141 2013-06-01T20:22:50Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Ferramenta-externa-3.png 6 69 142 2013-06-01T20:22:51Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Ferramenta-externa-2.png 6 70 143 2013-06-01T20:22:52Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Ferramenta-externa-1.png 6 71 144 2013-06-01T20:22:52Z Admin 1 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Exams-stress.jpg 6 72 149 2013-07-29T19:26:12Z Patrickts 2 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Cadastro de um Participante em um Curso 0 73 150 2013-07-29T19:26:40Z Patrickts 2 Created page with " [[File:exams-stress.jpg|400px]]" wikitext text/x-wiki [[File:exams-stress.jpg|400px]] ee5172818b8e585bd690b9ffc12d573b0cef3bde File:TryLogic-Moodle.png 6 74 152 2014-10-17T23:41:10Z Patrickts 2 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:TryLogic-Moodle-setting.png 6 75 153 2014-10-18T00:32:38Z Patrickts 2 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e TryLogic no Moodle 0 76 154 2014-10-18T00:44:42Z Patrickts 2 Created page with " * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/taskgenerator/index.php</code> * No item '''Chave do..." wikitext text/x-wiki * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/taskgenerator/index.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> [[File:TryLogic-Moodle-setting.png|650px]] 18e6c185a5d8aded7dc5fffd6c723861f598bbd8 155 154 2014-10-18T00:45:06Z Patrickts 2 wikitext text/x-wiki * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/taskgenerator/index.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> [[File:TryLogic-Moodle-setting.png|750px]] 40ffcfa00a567d9198f709044cbbde780aace445 157 155 2014-10-18T00:57:39Z Patrickts 2 wikitext text/x-wiki === ProofWeb via Moodle === * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/proofweb.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> === TryLogic - Tutorial === * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/trylogic.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> [[File:TryLogic-tutorial-Moodle-setting.png|750px]] === TryLogic - Gerador de Tarefa (DxR): apenas para instrutores === * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/taskgenerator/index.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> [[File:TryLogic-Moodle-setting.png|750px]] 6b800efe89c2c47c7151b0174406cb48808b9cb5 158 157 2014-10-18T00:58:13Z Patrickts 2 wikitext text/x-wiki === ProofWeb via Moodle === * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/proofweb.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> === TryLogic - Tutorial === * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/trylogic.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> [[File:TryLogic-tutorial-Moodle-setting.png|750px]] === TryLogic - Gerador de Tarefa (DxR): apenas para instrutores === * No item '''Lançamento de URL''' é preciso adicionar o endereço: ** <code>http://lolita.dimap.ufrn.br/trylogic-lti/taskgenerator/index.php</code> * No item '''Chave do consumidor''' é preico adicionar: ** <code>tryl0g1cLTI</code> * No item '''Segredo compartilhado''' é preciso adicionar: ** <code>secrettryl0g1cLTI</code> [[File:TryLogic-Moodle-setting.png|750px]] f8e8fe00dcca94e4a04c988e4fc4136b09df9ce4 File:TryLogic-tutorial-Moodle-setting.png 6 77 156 2014-10-18T00:51:23Z Patrickts 2 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Moodle-listadeusuarios.png 6 78 159 2014-10-18T01:07:16Z Patrickts 2 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Cadastro de um Participante em um Curso 0 73 160 150 2014-10-18T01:12:26Z Patrickts 2 wikitext text/x-wiki * Para cadastrar uma lista participantes em um curso é precio ir no seguinte caminho: ** <code>Minha página inicial ► Administração do site ► Usuários ► Contas ► Carregar lista de usuários</code> [[File:moodle-listadeusuarios.png|750px]] e76ad11eba8bc7128e50943f41e80c6f3bc926e8 161 160 2014-10-18T01:12:48Z Patrickts 2 wikitext text/x-wiki * Para cadastrar uma lista participantes em um curso é precio ir no seguinte caminho: ** <code>Minha página inicial ► Administração do site ► Usuários ► Contas ► Carregar lista de usuários</code> [[File:moodle-listadeusuarios.png|750px]] fffec12ce6fc52ad2399d11b2d65fb6c6cde10b1 163 161 2014-10-18T01:15:22Z Patrickts 2 wikitext text/x-wiki * Para cadastrar uma lista participantes em um curso é precio ir no seguinte caminho: ** <code>Minha página inicial ► Administração do site ► Usuários ► Contas ► Carregar lista de usuários</code> [[File:moodle-listadeusuarios.png|750px]] * É preciso submeter um arquivo csv (editável numa planilha com um separador pré-definido): [[File:moodle-inscrever-listadeusuarios-2.png|750px]] * É preciso que o arquivo tenha o seguinte formato: caf066634f3b753cc18fdad539e4cbf7f0fdc800 164 163 2014-10-18T01:33:46Z Patrickts 2 wikitext text/x-wiki * Para cadastrar uma lista participantes em um curso é precio ir no seguinte caminho: ** <code>Minha página inicial ► Administração do site ► Usuários ► Contas ► Carregar lista de usuários</code> [[File:moodle-listadeusuarios.png|750px]] * É preciso submeter um arquivo csv (editável numa planilha com um separador pré-definido): [[File:moodle-inscrever-listadeusuarios-2.png|750px]] * É preciso que o arquivo tenha o seguinte formato: <code> firstname,lastname,password,username,email,course1,role1 ALUNO,DE OLIVEIRA,alunodeoliveira,alunodeoliveira,email@email.com.br,FMC3-20142,5 PATRICK,CESAR TERREMATTE,patrick,patrick,patrickterrematte@yahoo.com.br,FMC3-20142,5 </code> * De modo que a coluna '''course1''' e '''role1''' sejam respectivamente o '''Nome breve do curso''' e o '''papel do participante no curso''': ** O '''Nome breve do curso''' é definido nas configurações do curso. ** A ordem do '''papel do participante''' segue a sequência da tabela em: <code>► Administração do site ► Usuários ► Permissões ► Definir funções </code>. Normalmente, o Administrador possui o id igual a 1, Professor é 3 e Aluno é 5. 950ec1ba7569a9a45971c84641375015b0d1e03c 165 164 2014-10-18T01:34:14Z Patrickts 2 wikitext text/x-wiki * Para cadastrar uma lista participantes em um curso é precio ir no seguinte caminho: ** <code>Minha página inicial ► Administração do site ► Usuários ► Contas ► Carregar lista de usuários</code> [[File:moodle-listadeusuarios.png|750px]] * É preciso submeter um arquivo csv (editável numa planilha com um separador pré-definido): [[File:moodle-inscrever-listadeusuarios-2.png|750px]] * É preciso que o arquivo tenha o seguinte formato: <code>firstname,lastname,password,username,email,course1,role1</code> <code>ALUNO,DE OLIVEIRA,alunodeoliveira,alunodeoliveira,email@email.com.br,FMC3-20142,5</code> <code>PATRICK,CESAR TERREMATTE,patrick,patrick,patrickterrematte@yahoo.com.br,FMC3-20142,5</code> * De modo que a coluna '''course1''' e '''role1''' sejam respectivamente o '''Nome breve do curso''' e o '''papel do participante no curso''': ** O '''Nome breve do curso''' é definido nas configurações do curso. ** A ordem do '''papel do participante''' segue a sequência da tabela em: <code>► Administração do site ► Usuários ► Permissões ► Definir funções </code>. Normalmente, o Administrador possui o id igual a 1, Professor é 3 e Aluno é 5. 52fffb89b7a3c37b5016391a2b2a9287fed0b5b5 166 165 2014-10-18T01:34:34Z Patrickts 2 wikitext text/x-wiki * Para cadastrar uma lista participantes em um curso é precio ir no seguinte caminho: ** <code>Minha página inicial ► Administração do site ► Usuários ► Contas ► Carregar lista de usuários</code> [[File:moodle-listadeusuarios.png|750px]] * É preciso submeter um arquivo csv (editável numa planilha com um separador pré-definido): [[File:moodle-inscrever-listadeusuarios-2.png|750px]] * É preciso que o arquivo tenha o seguinte formato: <code>firstname,lastname,password,username,email,course1,role1</code> <code>ALUNO,DE OLIVEIRA,alunodeoliveira,alunodeoliveira,email@email.com.br,FMC3-20142,5</code> <code>PATRICK,CESAR TERREMATTE,patrick,patrick,patrickterrematte@yahoo.com.br,FMC3-20142,5</code> * De modo que a coluna '''course1''' e '''role1''' sejam respectivamente o '''Nome breve do curso''' e o '''papel do participante no curso''': ** O '''Nome breve do curso''' é definido nas configurações do curso. ** A ordem do '''papel do participante''' segue a sequência da tabela em: <code>► Administração do site ► Usuários ► Permissões ► Definir funções </code>. Normalmente, o Administrador possui o id igual a 1, Professor é 3 e Aluno é 5. 3c5d5e3cd17d8d7103940c30421561b4e602e5d1 File:Moodle-inscrever-listadeusuarios-2.png 6 79 162 2014-10-18T01:13:19Z Patrickts 2 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Fundamentos Matemáticos da Computação 1 0 80 168 2015-11-23T21:16:13Z Patrickts 2 Created page with " [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]]" wikitext text/x-wiki [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]] 306664dbd17f306d88658270341afc154d4c0e13 169 168 2015-11-24T21:20:58Z Francleidepsimao 22 New link added: Propriedades de Somatório e Produtório wikitext text/x-wiki [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]] [[Propriedades de Somatório e Produtório]] 8e4da3b0019b5dec5889daf78708c9d753a5defd 170 169 2015-11-24T21:47:19Z Francleidepsimao 22 Renamed the link Propriedades Somatório e Produtório to Somatório e Produtório wikitext text/x-wiki [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]] [[Somatório e Produtório]] 32f2eb03d33d16e0d50bddf238287bc7174d461f Técnicas Avançadas de Contagem 0 81 171 2015-11-24T23:23:44Z Deeh 20 Created page with "Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. ..." wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. 19832c7c677f1e1dc00b7eb3feeaa3b1c1711f19 172 171 2015-11-24T23:27:13Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. '''1. Relações de recorrência''' Uma relação de recorrência descreve uma relação que um membro de uma sequência de an de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci satisfaz a relação de recorrência F_ {n} = F_ {n-1} + F_ {N-2} Juntamente com as condições iniciais F_ {1} = 1 e F_ {2} = 1, esta relação é suficiente para definir toda a seqüência \ {F_ {n} \}. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário r_} {n = f (N-R_ {1}, {N-R_ 2}, \ ldots, R_ {N-k}) 632e80ac55052d8b1f5923269340de391b5700ab 173 172 2015-11-24T23:47:16Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. '''1. Relações de recorrência''' Uma relação de recorrência descreve uma relação que um membro de uma sequência de an de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci satisfaz a relação de recorrência F_ {n} = F_ {n-1} + F_ {N-2} Juntamente com as condições iniciais F_ {1} = 1 e F_ {2} = 1, esta relação é suficiente para definir toda a seqüência \ {F_ {n} \}. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário r_} {n = f (N-R_ {1}, {N-R_ 2}, \ ldots, R_ {N-k}) em que cada termo r_ {n} da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é f (x, y) = y + x. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência \ r_ {{n} \} de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o r_ enésimo termo {n} de uma sequência de \ {r_ {n} \} seria convencionalmente escrito como r (n), e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência \ {r_ {n} \} como uma forma de representar uma função r cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número n é apenas r_ {n} = r (n). Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. 8c120f55b9e5ff57c442bbd9fc0f891f0a0bfd17 174 173 2015-11-25T12:29:57Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência de an de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci satisfaz a relação de recorrência F_ {n} = F_ {n-1} + F_ {N-2} Juntamente com as condições iniciais F_ {1} = 1 e F_ {2} = 1, esta relação é suficiente para definir toda a seqüência \ {F_ {n} \}. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário r_} {n = f (N-R_ {1}, {N-R_ 2}, \ ldots, R_ {N-k}) em que cada termo r_ {n} da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é f (x, y) = y + x. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência \ r_ {{n} \} de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o r_ enésimo termo {n} de uma sequência de \ {r_ {n} \} seria convencionalmente escrito como r (n), e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência \ {r_ {n} \} como uma forma de representar uma função r cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número n é apenas r_ {n} = r (n). Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. 307d82366a08a1b945b17025c52a7b7a41c065c2 175 174 2015-11-25T14:57:54Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência de an de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci satisfaz a relação de recorrência F_ {n} = F_ {n-1} + F_ {N-2} Juntamente com as condições iniciais F_ {1} = 1 e F_ {2} = 1, esta relação é suficiente para definir toda a seqüência \ {F_ {n} \}. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário r_} {n = f (N-R_ {1}, {N-R_ 2}, \ ldots, R_ {N-k}) em que cada termo r_ {n} da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é f (x, y) = y + x. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência \ r_ {{n} \} de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o r_ enésimo termo {n} de uma sequência de \ {r_ {n} \} seria convencionalmente escrito como r (n), e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência \ {r_ {n} \} como uma forma de representar uma função r cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número n é apenas r_ {n} = r (n). Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. '''== 1.1 Torre de Hanoi ==''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência H_{n} = 2H_{n - 1} + 1, \hspace{3ex} H_{1} = 1 é derivada, em que H_ {n} indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução H_} {n = 2 ^ {n} - 1 Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. 6419ef60b830503f899b6f21c5b9e915d4939c83 176 175 2015-11-25T14:58:18Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência de an de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci satisfaz a relação de recorrência F_ {n} = F_ {n-1} + F_ {N-2} Juntamente com as condições iniciais F_ {1} = 1 e F_ {2} = 1, esta relação é suficiente para definir toda a seqüência \ {F_ {n} \}. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário r_} {n = f (N-R_ {1}, {N-R_ 2}, \ ldots, R_ {N-k}) em que cada termo r_ {n} da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é f (x, y) = y + x. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência \ r_ {{n} \} de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o r_ enésimo termo {n} de uma sequência de \ {r_ {n} \} seria convencionalmente escrito como r (n), e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência \ {r_ {n} \} como uma forma de representar uma função r cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número n é apenas r_ {n} = r (n). Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência H_{n} = 2H_{n - 1} + 1, \hspace{3ex} H_{1} = 1 é derivada, em que H_ {n} indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução H_} {n = 2 ^ {n} - 1 Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. 5b4adf0919a9d6d62010b3f104c533540a893a99 181 176 2015-11-25T16:19:52Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência de an de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci satisfaz a relação de recorrência ''F''(<var>n</var>) = ''F''(<var>n-1</var>) + ''F''(<var>n-2</var>) Juntamente com as condições iniciais ''F''(<var>1</var>) = 1 e ''F''(<var>2</var>) = 1, esta relação é suficiente para definir toda a seqüência ''F''(<var>n</var>). Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário r_} {n = f (N-R_ {1}, {N-R_ 2}, \ ldots, R_ {N-k}) em que cada termo r_ {n} da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é f (x, y) = y + x. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência \ r_ {{n} \} de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o r_ enésimo termo {n} de uma sequência de \ {r_ {n} \} seria convencionalmente escrito como r (n), e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência \ {r_ {n} \} como uma forma de representar uma função r cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número n é apenas r_ {n} = r (n). Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência H_{n} = 2H_{n - 1} + 1, \hspace{3ex} H_{1} = 1 é derivada, em que H_ {n} indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução H_} {n = 2 ^ {n} - 1 Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. 0bc1ac92159271a1c0b4b5e82cd873c6a5aab970 182 181 2015-11-25T16:35:27Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência :<math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math>. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>. em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência \ r_ {{n} \} de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o r_ enésimo termo {n} de uma sequência de \ {r_ {n} \} seria convencionalmente escrito como r (n), e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência \ {r_ {n} \} como uma forma de representar uma função r cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número n é apenas r_ {n} = r (n). Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência H_{n} = 2H_{n - 1} + 1, \hspace{3ex} H_{1} = 1 é derivada, em que H_ {n} indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução H_} {n = 2 ^ {n} - 1 Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. b7e57dd3dcf2cf16b98893261b08b30a99031a5e 183 182 2015-11-25T16:46:19Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math>. Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math>. Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2); end: Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência H_{n} = 2H_{n - 1} + 1, \hspace{3ex} H_{1} = 1 é derivada, em que H_ {n} indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução H_} {n = 2 ^ {n} - 1 Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. 93e7c92aa79339fa10a3bf19e9420088bc28a2a8 184 183 2015-11-25T17:00:26Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre> Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. a12200084da999a775630129dedd5ef8658247a3 185 184 2015-11-25T17:03:59Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. f6ac56756cf60370594d1c628891775264f2b9f0 186 185 2015-11-26T01:19:42Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string)    printf (`Mova disco de peg% s para peg% s`,           src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. 12bb327eef61740e7a0e41793e971d86880b3dc7 187 186 2015-11-26T01:20:19Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`,           src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. 066c52ea8d035052b5deb3c2402734b3c319820f 188 187 2015-11-26T01:20:44Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. 689ebd519b92c0a3edbd3460d7e49e0c77ca281d 189 188 2015-11-26T02:08:49Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em um procedimento de alto nível, Hanoi, proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa Hanoi program consegue exibir uma solução específica para o enigma das Tcan exhibit a specific solution to the Torres de Hanoi para qualquer número n de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores n de discos para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de n discos. '''2. Resolução de recorrências com Maple''' Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> \ {r_ {n} \} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de r_ {k}, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. 08cb53bb003ee855f5e3d5738f2e804aa928e7b4 190 189 2015-11-26T02:17:17Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em um procedimento de alto nível, Hanoi, proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa Hanoi program consegue exibir uma solução específica para o enigma das Tcan exhibit a specific solution to the Torres de Hanoi para qualquer número n de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores n de discos para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de n discos. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de r_ {k}, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de sua polinômio característico.l <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 \hspace{3em}\mbox{and}\hspace{3em} r_{2} = 2 </math> Então sua equação caracerística é Then its characteristic equation is <math> x^{2} - 2x - 3 </math> b95d953cfa8ac0d550052933d2cd12279615b459 191 190 2015-11-26T02:30:23Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em um procedimento de alto nível, Hanoi, proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa Hanoi program consegue exibir uma solução específica para o enigma das Tcan exhibit a specific solution to the Torres de Hanoi para qualquer número n de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores n de discos para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de n discos. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de r_ {k}, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de sua polinômio característico.l <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 \hspace{3em}\mbox{and}\hspace{3em} r_{2} = 2 </math> Então sua equação caracerística é Then its characteristic equation is <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que satisfazem a equação quadrática. <math> ''x ^ 2-2 * x - 3 = 0.'' </math> Agora que o Maple aponta que as soluções são x = 3 e x = -1, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \ alpha 3 ^ {n} + \ beta (-1) ^ {n} </math> e7feb0e1d303ba52ec78ec4f0663d064836ebfda 192 191 2015-11-26T02:33:26Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de Hanói, a rotina utilitária PrintMove , e o mecanismo recursivo do programa TransferDisk, que faz a maioria do trabalho. A parte mais fácil de escrever é a função PrintMove, que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando printf da biblioteca do Maple, que pode ser usado para saída formatada. A função printf tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo TransferDisk faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em um procedimento de alto nível, Hanoi, proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa Hanoi program consegue exibir uma solução específica para o enigma das Tcan exhibit a specific solution to the Torres de Hanoi para qualquer número n de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores n de discos para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de n discos. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de r_ {k}, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de sua polinômio característico.l <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 \hspace{3em}\mbox{and}\hspace{3em} r_{2} = 2 </math> Então sua equação caracerística é Then its characteristic equation is <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que satisfazem a equação quadrática. <math> ''x ^ 2-2 * x - 3 = 0.'' </math> Agora que o Maple aponta que as soluções são x = 3 e x = -1, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \ alpha 3 ^ {n} + \ beta (-1) ^ {n} </math> onde \ alpha e \ beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \ alpha e \ beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3 \ alpha - \ beta = 4 </math> <math> 3 ^ {2} \ alpha + \ beta = 2 </math> 9e19dd6a0a6b4af6e0053a0138545a4c522bd84f 193 192 2015-11-26T13:18:11Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> 51e04fb7f00b790e97043a65ad24c9965cfc9259 194 193 2015-11-26T13:28:03Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ==='''3. Relações de recorrência'''=== terceira parte 6ab6ea6f881f66ef669aaaa69b8c2028f92b2a47 195 194 2015-11-26T13:28:39Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> 51e04fb7f00b790e97043a65ad24c9965cfc9259 201 195 2015-11-30T02:12:36Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; 2721c59bc1332bfb3d2f19c179b69dfb6316c087 202 201 2015-11-30T03:02:01Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: 83de0d9e3dc5372d1d0ec583eda46b6d252a3752 203 202 2015-11-30T03:12:35Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); 8b382db5e75a6d5103d6369442337dd878f37888 204 203 2015-11-30T11:38:26Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. bd2940d9678d79ece6b5105d53e400e176649654 205 204 2015-11-30T13:07:24Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),     R (1) = 2, r (n)); simplify(%); af0a461aaa091e1ca51aa6e317f4e6134941ff22 206 205 2015-11-30T13:10:46Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); 5ff2d3338235a1e584c0d090155f094306aebd84 207 206 2015-11-30T13:33:31Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de divisão e conquer''' Um bom exemplo de relações de divisão e conquer é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. 556b707ad030885ed2a8f9f2ec15efa6add78faa 208 207 2015-11-30T13:49:16Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de divisão e conquer''' Um bom exemplo de relações de divisão e conquer é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte aço com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência divide and conquer são geradas. Em geral, uma relação de recorrência do tipo dive and conquer tem a forma <math> r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação divide and conquer. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); 169fd317364cb65ef252ad02747992451ed18d2d 209 208 2015-12-02T11:58:50Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de dividir e conquistar é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte aço com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência divide and conquer são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios. Em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A ''''função geradora'''' <math> g(x)</math> para uma sequência <math>\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma seqüência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); 746e0b01acfb488055c205d9bf1d44cff3b8c3b8 210 209 2015-12-02T12:08:16Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de dividir e conquistar é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte aço com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência divide and conquer são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios. Em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma seqüência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); 63ce9d3020698f6ecb8c1872681dc80660b27d1e 211 210 2015-12-02T12:32:17Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de dividir e conquistar é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte aço com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência divide and conquer são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios. Em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma seqüência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a seqüência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formal. Isto é feito como se segue. Para Maple, cada série de potências formal é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formal, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potência formal f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa seqüência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. a12623111a1de165720e209c883451706590119a Somatório e Produtório 0 82 179 2015-11-25T15:28:13Z Patrickts 2 Created page with " == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n)\text{, onde C é uma constante. </math> <math> \sum_{n=s}^t f(n) + \sum_{n=..." wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n)\text{, onde C é uma constante. </math> <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n) \text{, note que } s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2}, \text{ progressão aritmética.} </math> <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> b7800b3ad93b08b63c4e893e2788a0b909e5ce10 180 179 2015-11-25T15:29:19Z Patrickts 2 /* Propriedades de Somatório */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> a9d59cee887343792168c848a4ff2aa01f4b61ce 196 180 2015-11-29T21:17:52Z Francleidepsimao 22 Organização do assunto em link wikitext text/x-wiki [[Propriedades de Somatório]] 2aae9d949101048c98431bab49241a55b197dfd1 198 196 2015-11-29T21:20:54Z Francleidepsimao 22 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> 2af7eb7c2e8b85aa7aa9966f496e8eca8e933bc1 199 198 2015-11-29T23:25:15Z Francleidepsimao 22 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====F#==== 307eb7f8d1523a5c172cad410b2ccc84711e8963 200 199 2015-11-29T23:26:14Z Francleidepsimao 22 /* Propriedades de Somatório */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====F#==== 07fb9d4f25a9b855df5b091ace21c799a9830e66 Propriedades de Somatório 0 83 197 2015-11-29T21:17:59Z Francleidepsimao 22 Created page with "== Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n..." wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> 2af7eb7c2e8b85aa7aa9966f496e8eca8e933bc1 Técnicas Avançadas de Contagem 0 81 212 211 2015-12-02T12:41:39Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de dividir e conquistar é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte aço com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência divide and conquer são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios. Em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma seqüência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a seqüência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formal. Isto é feito como se segue. Para Maple, cada série de potências formal é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formal, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potência formal f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa seqüência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> 60dadfed7c541d255325c12737ebf24bd0d05de9 213 212 2015-12-02T13:01:14Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começamos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostramos como resolver o enigma da Torre de Hanói e encontramos o número de movimentos necessários para n discos. Descrevemos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não-homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Mapple. Nós ilustramos o uso dessa resolução geral demonstrando como usa-la para resolver divide and conquer relações de recorrência. Depois de estudar relações de recorrência, vamos mostrar como usar bordo para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutimos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da seqüência que o precedem. Por exemplo, a famosa seqüência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a seqüência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem na seqüência. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na seqüência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a seqüência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanói para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, e descrevendo-os para nós. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanói''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um Peg para outro. Mas, porque é recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, empacotamos em uma procedure de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma seqüência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma núnica solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação caracerística é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algomuito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Paraobservar como funciona, iremos tentar alguns casos de teste. Paraconstruir uma função para calcular a sequencia Fibonacci chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante ode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com eigenvalues????? evals := [solve(char_eqn, x)]; No geral, para testar um eigenvalue repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raizes distintas aparece. O nth termo da sequencia, quando á um double eigenvalue, é dado ṕor: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raizes repetidas, e então faz o calculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <maaath> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser extendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math>\ alpha_ {n}, \ alpha_ {n-1}, \ ldots, \ alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}<math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math>\ c_ {{n} \} </math> pela seqüência zero: <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanói <math>H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanói com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>\ alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = \ alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>\ alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanói é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvedor de recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(\ sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes 2. sistemas de relações de recorrência lineares com coeficientes constantes 3. dividir e conquistar???? relações de recorrência com coeficientes constantes 4. muitas relações de recorrência lineares de primeira ordem 5. algumas relações de recorrência não-lineares de primeira ordem As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, é simplesmente retorna unevaluated. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semeslhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de dividir e conquistar é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte aço com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência divide and conquer são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – o princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a idéia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalbMaple avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamnho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positicos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros postivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisiveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção.) A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os numeros menores que 10000 que sao divisíveis por todos os quarto número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios. Em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma seqüência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a seqüência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formal. Isto é feito como se segue. Para Maple, cada série de potências formal é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formal, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potência formal f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa seqüência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> c1a38792d99722975399aa358deeb866442f7800 214 213 2015-12-02T21:48:24Z Aclunga 29 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u \hspace{3em}\mbox{and}\hspace{3em} r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_} {n = 4 r_ {N-1} - 4 r_ {N-2} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\ alpha_ {n} r_ {n} + \ alpha_ {n-1} r_ {n-1} + \ cdots + \ alpha_ {nk} {r_ nk} = c_ {n} </math> onde <math> /alpha_ {n}, /alpha_ {n-1}, \ ldots, /alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_ {n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_ {n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>/alpha_ {n} r_ {n} + /alpha_ {n-1} r_ {n-1} + \ cdots + /alpha_ {nk} {r_ nk} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_ {n} = n3 ^ n </math> é uma solução para a relação de recorrência <math>r_ {n} = 3r_ {n-1} + 3 ^ n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_ {0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>{H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> f6437fb81bbc16f9675b88fd9873492e7cb867a2 215 214 2015-12-03T19:59:38Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_ {n}, \alpha_ {n-1}, \ ldots, \alpha_ {nk} e c_ {n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>{H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> aa80e8338b08b7602cc9be478da675b1931215c6 216 215 2015-12-03T20:00:43Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ ldots, \alpha_{n-k} e c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>{H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> 65ac8866cdc74cf19d7b9065bfc0cb5265638169 217 216 2015-12-03T20:01:24Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (R (0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r (n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>{H_} {n = 2H_ {N-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_ {1} = 1.</math> A relação de recorrência homogênea associada é <math>h_ {n} = 2 h_ {n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_ {n} = \ alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> feee3d21af159eaa8d3c31a66f0b93212b7348fb 218 217 2015-12-03T20:03:30Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve em Maple para fazer isso. solve (x ^ 2-2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x ^ 2-2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes \alpha e \beta. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3 ^{2} \alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> 79670d70d7f3ba5886a0cb4e67868b459e128b45 219 218 2015-12-03T21:24:02Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de movimentos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> e62a65869a5bccfeb98a8e00eb72452aae92c71f 220 219 2015-12-03T21:24:52Z Paulohq 21 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> c370dfdfbdb388caed35a01b07fb86a8be1528c3 227 220 2015-12-08T12:49:42Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> '''5. Cálculos e como explorá-los''' Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. c55092f3c2d7bf70c2618954afa6a3e673295ffb 228 227 2015-12-08T13:22:44Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> '''5. Cálculos e como explorá-los''' Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). 3944afb1f25d423d34d3bfc01b77b7c15e45159a 229 228 2015-12-08T14:00:05Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> '''5. Cálculos e como explorá-los''' Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. e866995fd01de6c3f51a18da9175596421ccf145 230 229 2015-12-08T14:24:24Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); '''3. Inclusão – Exclusão''' Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; '''4. Funções geradoras''' Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> '''5. Cálculos e como explorá-los''' Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em uma matriz obtida com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor na matriz ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. 82b91fd190947dc613d9fe09c6c5f812d9018bc9 231 230 2015-12-08T14:36:33Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); 49e202d999a1dfe821f0053fe4201be5ead0e203 232 231 2015-12-08T16:28:17Z Deeh 20 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); e329e03fa38cc517d28e5ef5fd26a3ce6ffdc7dc 236 232 2015-12-08T22:42:55Z Clah 19 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''5. Relações de recorrência'''=== ==='''6. Exemplos Extras'''=== Exemplo 1 (página 415) Resolva: <math>a_n = 2a_{(n-1)} + 3a_{(n-2)}, a_{(0)} = 0, a_{(1)} = 1 </math> ''Solução'': Usando <math> a_{(n)} = r^n </math>, a equação característica a seguir é obtida: <math>r^2 – 2r – 3 = 0 </math> Os fatores do lado esquerdo como <math>{(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_{(n)} = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_{(0)} = 0</math> e <math>a_{(1)} = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c+d = 0</math> <math> 3c-d = 1</math> Com solução de <math> c = \left(\frac{1}{4} \right)</math> e <math> d = \left(\frac{-1}{4} \right)</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_{(n)} = \left(\frac{1}{4} \right).3^n \left(\frac{-1}{4} \right).(-1)^n</math> 748917a948c7256d87d05c66828e1f576e5de177 237 236 2015-12-08T22:45:08Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''5. Relações de recorrência'''=== ==='''6. Exemplos Extras'''=== Exemplo 1 (página 415) Resolva: <math>a_n = 2a_n-1 + 3a_n-2, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math>r^2 – 2r – 3 = 0 </math> Os fatores do lado esquerdo como <math>{(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \left(\frac{1}{4} \right)</math> e <math> d = \left(\frac{-1}{4} \right)</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \left\frac{1}{4} \right.3^n \left\frac{-1}{4} \right.(-1)^n</math> e064a90f219c3eadd54615d4cdad31ee0484fda9 238 237 2015-12-08T22:47:13Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''5. Relações de recorrência'''=== ==='''6. Exemplos Extras'''=== Exemplo 1 (página 415) Resolva: <math>a_n = 2a_n-1 + 3a_n-2, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math>r^2 – 2r – 3 = 0 </math> Os fatores do lado esquerdo como <math>{(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = \frac{-1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n \frac{-1}{4}.(-1)^n</math> b4bedde671302b62a55ff4b0d3c86d2e8bc4ce4a 239 238 2015-12-08T22:51:21Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''5. Relações de recorrência'''=== ==='''6. Exemplos Extras'''=== Exemplo 1 (página 415) Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 – 2r – 3 = 0 </math> Os fatores do lado esquerdo como <math>{(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = \frac{-1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> 0bace4d102ad2d5c44b3ee461b75520c8a2bd5ec 240 239 2015-12-08T22:53:10Z Clah 19 /* 5. Relações de recorrência */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== Exemplo 1 (página 415) Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 – 2r – 3 = 0 </math> Os fatores do lado esquerdo como <math>{(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = \frac{-1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> 8ce6c80a574266fd97bfe4bb1b4de1672113a5a1 241 240 2015-12-08T22:54:27Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> {r^2 – 2r – 3 = 0} </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = \frac{-1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> b15e687547bce2cb8e2a03af84e5f7074b15b688 242 241 2015-12-08T22:55:24Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> {r^2 – 2r – 3 = 0} </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = \frac{-1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> 408e8c05462f39b6bbb0b0cd1d5da6dd5c38d12c 243 242 2015-12-08T22:56:22Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_ {{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> {r^2 – 2r – 3 = 0} </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> b95d0858c605939daf81e8e72aa95ecd0f2b99e2 244 243 2015-12-08T23:35:20Z Clah 19 /* 4. Funções geradoras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> {r^2 – 2r – 3 = 0} </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c3^n + d(-1)^n = 0</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> ca9e9952b5d6757c11b91a2f20ea06ee51aea4bc 245 244 2015-12-09T00:03:15Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 – 2.r – 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> f6115ec3849a900125b030f783d071f8890944be 247 245 2015-12-09T00:23:24Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 – 2.r – 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' 41f571d3d6176a09af7e69f09df1c6d5df4190f5 248 247 2015-12-09T00:24:49Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' 4b7955fc6853b8c2d7f7c7b1ea296cf526ea0b72 250 248 2015-12-09T00:32:25Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' 09ccb5cbcc5a28d6fe18d08bc1105ae08d3eebff 251 250 2015-12-09T00:49:25Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1</math>, <math>a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math> = 3{(a_{n-2} + 1)}+1</math> <math> = 3^2a_{n-2} +3 . 1+1</math> <math> = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math> = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math> = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math> = 4 . 3^n + /frac{3^n-1}{2} </math> <math> = /frac{8 . 3^n}{2} + /frac{3^n}{2} - /frac{1}{2} </math> <math> = /frac{9 . 3^n}{2} - /frac{1}{2} </math> <math> = /frac{3 ^23^n}{2} - /frac{1}{2} </math> <math> = /frac{3 ^n+2}{2} - /frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> c1f36991ec0bad5654874394ed47e45a3881e9d2 252 251 2015-12-09T00:52:03Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math> = 3{(a_{n-2} + 1)}+1</math> <math> = 3^2a_{n-2} +3 . 1+1</math> <math> = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math> = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math> = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math> = 4 . 3^n + /frac{3^n-1}{2} </math> <math> = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math> = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math> = \frac{3 ^23^n}{2} - \frac{1}{2} </math> <math> = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> 7ec435c62b6f671cbc36ad3d18540c377272be8e 254 252 2015-12-09T00:58:46Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + /frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^23^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> d4bfe6ddb1a93029d4092aedd6a8bff178306da0 256 254 2015-12-09T01:13:01Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3ª_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3ª_{n-1}</math>. Esta equação característica é <math> r – 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math> a_n = a3^n</math> . Para obter uma solução específifca para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. dac5e915f061c8ffb9cfd5383743c29218c56646 257 256 2015-12-09T01:14:50Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3ª_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específifca para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. 8dcbd6514162ae107335ed91f0d2e9fac613c632 258 257 2015-12-09T01:23:06Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math> a_n = a3^n – 2^{n+a}</math>. A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^0 – 2^{0+1}</math> , ou <math>2 = a – 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^n – 2^{n+1}</math> . '''Exemplo 7 (página 415)''' dff8c6a36710b2ec5c9812c8d32aac55222bcbea Somatório e Produtório 0 82 221 200 2015-12-04T16:29:23Z Francleidepsimao 22 add Principais representacoes wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_i*y_i = x_1*y_1+x_2*y_2+...+x_n*y_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)*(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)*(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====F#==== ---- ==Referências== 54068df3ae424519aa143066d7ad2d3af4b4b860 222 221 2015-12-04T16:33:06Z Francleidepsimao 22 /* Soma de produtos */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)*(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)*(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====F#==== ---- ==Referências== 8c55ee2934b84c4d47a1babbb6d8b94c9ec2e285 223 222 2015-12-04T16:33:22Z Francleidepsimao 22 /* Produtos das somas */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====F#==== ---- ==Referências== 29ddad0a089d953709000f7837365fb3a3798dcc 224 223 2015-12-08T02:53:53Z Jaimerson 27 /* Somatório em Linguagem Funcional */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====Elixir==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== b3fa1a08166dd0a1681cef8ebb5456616c0e0857 225 224 2015-12-08T02:58:58Z Jaimerson 27 /* Referências */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====Elixir==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 2cbb142af92236f947137087e26ab1e47af6bb9c 226 225 2015-12-08T02:59:42Z Jaimerson 27 /* Elixir */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> c1eaad2356b6550b09f6bcaad0e81fdf779dd804 Contagem 0 84 233 2015-12-08T20:28:38Z Igorolivei 26 Created page with "\usepackage{amsmath} A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teor..." wikitext text/x-wiki \usepackage{amsmath} A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ===1. Funções Maple relevantes=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);'''''<br /> '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);''''' '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);''''' '''''allstructs(Combination([apple,orange,pear]), size=2);''''' '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. 4fd98434ff4a9f4436b8afda61d4e37b85bafcec 234 233 2015-12-08T20:47:09Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />'''''<br /> '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. 2885a47f75165d7ed3e26e19065f37edd14f90ca 235 234 2015-12-08T20:57:07Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. 338d726e2e342dcd319b417c3f166f2089108c8f 246 235 2015-12-09T00:11:32Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' 5afb52a24790d1b4eeb18060347a5e655e5bba59 249 246 2015-12-09T00:29:01Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' 7ef3b4c543aba886f6a10455633564a979d894ef 253 249 2015-12-09T00:55:21Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' b0e0f1d70661cbd186e542e14985f70325ed02bf 255 253 2015-12-09T01:04:18Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. e9a7a692848bbe65cf981473ddafba6d6726478c 259 255 2015-12-09T01:24:57Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== 1. (Projetos de computador) Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' 2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto . Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' 3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos. Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. 464bbfc49ec2681341da658be62011f9f7ae9eae 260 259 2015-12-09T01:32:55Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. (Projetos de computador) Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' 6737cb4c7c40ec05fb9a94c67b371a91754265b8 261 260 2015-12-09T01:35:28Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. (Projetos de computador) Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. 003dd9b6766856a9bcbd39e1c2efc59aaa5ef087 Contagem 0 84 262 261 2015-12-09T01:40:52Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. (Projetos de computador) Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' 6e8384fb872cabcedeea4e84d5edd82ede35a13c 264 262 2015-12-09T01:47:42Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 79edf8da26bc07c8934c92c1df7408555ddc0f17 265 264 2015-12-09T01:55:19Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. 147ce289a3be66642d12137da44df0fcc3565704 274 265 2015-12-09T02:43:44Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: 251404299964ae25062c506e594cda7437083b32 275 274 2015-12-09T02:53:48Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> 26aa2e7f0e02f0dd8e60f1dfe7fd1b4aeeb5bcb1 277 275 2015-12-09T03:30:18Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. Solução: pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. Solução: Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. c3172a65eed8319df27a27eae42ab86ea409a62e 278 277 2015-12-09T03:34:11Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. Solução: pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. Solução: Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> Solução: Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? Solução: Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> b1a78c198f49a40e3a7194f798a53ba32382dbc9 279 278 2015-12-09T03:46:26Z Igorolivei 26 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. Solução: pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. Solução: Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> Solução: Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? Solução: Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. Solução: A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? Solução: Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> Solução: Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. Solução: Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. Solução: Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. Solução: Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? Solução: Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 1e2ca804a45e3dd606d8ebf2e6314cf6ef448486 281 279 2015-12-09T04:21:31Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. Solução: pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. Solução: Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> Solução: Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? Solução: Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. Solução: A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? Solução: Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> Solução: Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. Solução: Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. Solução: Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. Solução: Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? Solução: Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. f47807fa825fe82c311ab80e5e6feeb4b374a660 286 281 2015-12-09T11:49:09Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. Solução: pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. Solução: Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> Solução: Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? Solução: Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. Solução: A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? Solução: Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. b5228d706b386a8e399619d29d034c4f8519c38c 287 286 2015-12-09T11:52:15Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. Solução: pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. Solução: Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> Solução: Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 5b31cfff8a9673e14e5c0c6402d3eaa26ada0d60 288 287 2015-12-09T11:59:31Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? Solução: (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? Solução: Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? Solução: O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. Solução: Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; Solução: a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. Solução: a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. d60c1e4ae2a1c460b31ee7da649bd349d0dc13e3 289 288 2015-12-09T12:00:33Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 07cde8fe16b023b7254817ddeee053b20828b9fa 290 289 2015-12-09T12:01:33Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. Solução: Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. Solução: Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. Solução: O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. Solução: O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. Solução:De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 14cbb571860e1ef3efdf3db3ddb1a4f36ef8a443 291 290 2015-12-09T12:06:09Z Igorolivei 26 /* Exemplos da Seção 4.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? Solução: Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim Solução: Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (C) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. Solução: para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). Solução: a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. a238bc2194f53d944b89b593cd9c80eb8fd0ebd6 292 291 2015-12-09T12:09:19Z Igorolivei 26 /* Exemplos extras da seção 4.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? Solução: Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 224331a821b31935d2dfdf45a698f9abd7554756 293 292 2015-12-09T12:11:59Z Igorolivei 26 /* Exemplos extras da seção 4.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2x1 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. ff89edc58e83e711d32a7f81d0389376f2442e88 294 293 2015-12-09T12:13:40Z Igorolivei 26 /* Exemplos extras da seção 4.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote “combinat” contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, “combstruct”, disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote “combinat” faz. O pacote “combstruct” fornece funções “interstructs”. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que “combstruct” pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote “combstruct”, digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando “with(share)”, já que o pacote “combstruct” é parte da biblioteca na versão 3. As funções no pacote “combinat” para combinações são “numbcomb”, “choose”, e “randcomb”. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função “numbcomb” conta o número de combinações (ou r-combinações) de um conjunto. A função “choose” lista as combinações. Portanto sempre existirão elementos “numbcomb” listados por “choose”. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. a0307fcbba96867b6295dd05897fb17ade7dec74 295 294 2015-12-09T14:57:37Z Igorolivei 26 /* 1. Funções Maple relevantes */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função “binomial” da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, “binomial” resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, “binomial” retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável “n” chamando “expand”. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado “convert”. '''''convert(binomial(n, k), factorial);''''' O procedimento “convert” é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento “binomial”, para uma equivalente expressada usando fatoriais. Devido a “convert” aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre “convert”, é a página principal de ajuda para este comando, acessada digitando “?convert”. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos “convert”. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 6b5aec50e8f5e5c3b7f88f6974dfb75f635fcccc 296 295 2015-12-09T16:04:19Z Igorolivei 26 /* 2.1. Coeficientes binomiais */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento 'convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 4c751e84ab3c7b1a59c5c3c78aaa339d60707ddb 297 296 2015-12-09T16:04:41Z Igorolivei 26 /* 2.1. Coeficientes binomiais */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função “coeff” que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial “p” e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene p em q ##crie uma lista r de termos multinomiais em q. ##crie um multiset m consistido de multinomiais em q com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando term e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para “mcoeff”. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 9fc89752810d84b414e2d3a0ed678417504f5e2c 298 297 2015-12-09T16:06:19Z Igorolivei 26 /* 2.2. Coeficientes multinomiais */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista m para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. b30f82ec2f0ed4f8c89d767f1505983e007e8123 299 298 2015-12-09T16:06:43Z Igorolivei 26 /* 2.2. Coeficientes multinomiais */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau “n” é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 0912a49f0be9a92de10c95e8b3f5bce44e74887b 300 299 2015-12-09T16:07:27Z Igorolivei 26 /* 2.3. Números Stirling */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são “numbperm”, “permute” e “randperm”. Já que todas estão no pacotes “combinat”, devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote “combstruct”, esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função “subsets” permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função “subsets” retorna uma tabela que contém duas entradas. Uma é chamada “nextvalue”, e é um procedimento para gerar a próxima combinação, e a outra é “finished”, uma flag true/flase que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando “combstruct”, uma faz a mesma coisa usando a função “iterstructs”. O procedimento “iterstructs” também retorna uma tabela, mas dessa vez usa as funções “next” e “finished” para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando “iterstructs”, podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: numbperm([M,I,S,S,I,S,S,I,P,P,I]); A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. numbperm([O,R,O,N,O],0); Usando o pacote “combstruct”, nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento “numbpart”. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função “partition”. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. c90d3ab342583855aeb6ba112537a85e9dc6b489 301 300 2015-12-09T16:10:03Z Igorolivei 26 /* 3. Permutações */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função “evalf” - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para “n” tentativas Bernoulli, cada uma com a probabilidade “p” de sucesso, é “np”. Nós usaremos “EV” para denotar o valor esperado em Maple. (Nós não podemos usar “E” porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 9812319c557d65b8fcdb522ecde5e91ff8189009 302 301 2015-12-09T16:11:15Z Igorolivei 26 /* 4. Probabilidade discreta */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a “next” combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote “combstruct”, você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 2afe17cd82a0d8d2b2fcdf7b283b13371eeed0fd 303 302 2015-12-09T16:12:06Z Igorolivei 26 /* 5. Gerando combinações e permutações */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecânicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de “n” números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento “numbcomb” no pacote “combinat”. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 08077db9b331c61df50384e5c1615bd7b750648e 304 303 2015-12-09T16:13:14Z Igorolivei 26 /* 1. Dado um inteiro positivo “n”, encontre a probabilidade de selecionar seis inteiros do conjunto {1, \cdots , n} que foram mecânicamente selecionados em uma loteria. */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado “permute” para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (while) corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. f5c629aaec818726b55b3e733292e8d73f85e9f8 305 304 2015-12-09T16:20:41Z Igorolivei 26 /* 3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos. */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função “choose” do Maple (no pacote “combinat”), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece “r” vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O “i $ r” significa repetir “i” r vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. fa1bb12e79131dae365a41ebaf5e76080b2aa273 306 305 2015-12-09T16:25:44Z Igorolivei 26 /* 2. Dados inteiros positivos “n” e “r”, liste todas as r-combinações, com repetições permitidas, do conjunto . */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como “p”, nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 3de02598a4236cb2f9cec2f51f8d4696721d115f 307 306 2015-12-09T16:26:39Z Igorolivei 26 /* 6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcen... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é “p”, observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 1f06ed3d38738ae017202e818cc6402449cab1e2 308 307 2015-12-09T16:26:53Z Igorolivei 26 /* 6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcen... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== Solução Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple “rand” para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple “igcd” em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado “RandPairs”: '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. be77be0a6cdd514080fc1cbaa49bc62d63f64e90 309 308 2015-12-09T16:27:56Z Igorolivei 26 /* 5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados co... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto “n” cresce.===== Solução Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função “ifactors” (o “i” significa “integer”) para fatorar “c”. Essa função é uma das várias do Maple que deve ser definida “readlib” antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para “ifactors” explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função “max” para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado “n”, retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de “n”, e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de “n” tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de “n”. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 569e9250dd8315e6749740b51e8a76a0a893e861 310 309 2015-12-09T16:30:42Z Igorolivei 26 /* 4. Nós queremos olhar para os coeficientes binomiais C(2n, n). Especificamente, para muitos exemplos, nós queremos determinar se C(2n, n) é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites e... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== Solução Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 3c39a87b6b3acf2842b4d1be2cfbdef7e5d57a0c 311 310 2015-12-09T16:31:49Z Igorolivei 26 /* 6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcen... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 773808ce0134f2ae87e82be832f2819e0d6a4299 Técnicas Avançadas de Contagem 0 81 263 258 2015-12-09T01:45:24Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' 02272efb230827561e78aaa8e06e711ba460e68b 266 263 2015-12-09T01:56:43Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} – 12a_{n-2} + 3n</math> , com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. . Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math> , obtendo <math>cn + d = 8 {[c{(n-1)} + d]} – 12 {[c{(n-2)} + d]} + 3n </math> Em que pode ser reescrito como <math>n {(c – 8c + 12 c - 3)} + {(d + 8c – 8d -24c + 12d)} = 0 </math> 7f7357d07c80ca50abc545078b68fc445e0d4d4d 267 266 2015-12-09T02:09:41Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12 c - 3) + (d + 8c – 8d -24c + 12d) = 0</math> da39711061af94ac84d9b1204c1bfcf3bb3a46c3 268 267 2015-12-09T02:22:18Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12 c - 3) + (d + 8c – 8d -24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c – 3 = 0</math> <math>d + 8c – 8 d – 24c _ 12d = 0</math> ou <math>5 c – 3 = 0</math> <math>-16c + 5d = 0</math> e48930f7c45532f67c9f5918aaaaab3086c71902 269 268 2015-12-09T02:26:49Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c – 3 = 0</math> <math>d + 8c – 8d – 24c - 12d = 0</math> ou <math>5c – 3 = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> ceb7cec7e7456b990e2292e20e21c7658aed2ffd 270 269 2015-12-09T02:28:35Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c – 3 = 0</math> <math>d + 8c – 8d – 24c - 12d = 0</math> ou <math>5c – 3 = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> 9574f882d0e2b69f44909d4f014b026bbb7d06f2 271 270 2015-12-09T02:33:22Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c – 3 = 0</math> <math>d + 8c – 8d – 24c - 12d = 0</math> ou <math>5c – 3 = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo a=27/255 e b=-2. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> 4c53dc7890c702fd8d40335ff220d6d4deb4594a 272 271 2015-12-09T02:39:19Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é c= 3 e d=-11/5. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c - 3 = 0</math> <math>d + 8c – 8d – 24c + 12d = 0</math> ou <math>5c – 3d = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo a=27/255 e b=-2. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> de40cc121328b98ef111cab87d36f4a4d763667d 273 272 2015-12-09T02:43:21Z Clah 19 /* 6. Exemplos Extras */ wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. ==='''1. Relações de recorrência'''=== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ''' 1.1 Torre de Hanoi ''' O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. ==='''2. Resolução de recorrências com Maple'''=== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ''' 2.1. Uma relação de recorrência linear homogênea com coeficientes constantes''' Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); '''2.2. Relações de recorrência heterogêneas''' Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. '''2.3. Resolvendo recorrências em Maple''' Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); '''2.4. Relações de dividir e conquistar''' Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); ===='''3. Inclusão – Exclusão'''=== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; ==='''4. Funções geradoras'''=== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> ==='''5. Cálculos e como explorá-los'''=== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); '''2. Encontrar tantas números de Fibonacci primos que puder.''' '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. '''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.''' '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); ==='''6. Exemplos Extras'''=== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é <math>c= 3</math> e <math>d = -\frac{11}{5}. Assim, a solução para a relação de recorrência é <math> a_n = 3 . 5^n - \frac{11}{5}.n.5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c - 3 = 0</math> <math>d + 8c – 8d – 24c + 12d = 0</math> ou <math>5c – 3d = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo <math>a = \frac{27}{255}</math> e <math>b = -2</math>. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> 566c93358135fb6b3d0d92f20e7d97c277c01d70 276 273 2015-12-09T02:58:26Z Clah 19 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. =='''Relações de recorrência'''== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ===''' Torre de Hanoi '''=== O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. =='''Resolução de recorrências com Maple'''== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ==='''Uma relação de recorrência linear homogênea com coeficientes constantes'''=== Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); ==='''Relações de recorrência heterogêneas'''=== Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N} = {-1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. ==='''Resolvendo recorrências em Maple'''=== Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math>\ {r_ {n} \} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); ==='''Relações de dividir e conquistar'''=== Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> {r_} {n = a r_ {n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); =='''Inclusão – Exclusão'''== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; =='''Funções geradoras'''== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> =='''Cálculos e como explorá-los'''== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); ==='''2. Encontrar tantas números de Fibonacci primos que puder.'''==== '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. ==='''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.'''=== '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); =='''Exemplos Extras'''== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é <math>c= 3</math> e <math>d = -\frac{11}{5}</math>. Assim, a solução para a relação de recorrência é <math>a_n = 3 . 5^n - \frac{11}{5} . n . 5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c - 3 = 0</math> <math>d + 8c – 8d – 24c + 12d = 0</math> ou <math>5c – 3d = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo <math>a = \frac{27}{255}</math> e <math>b = -2</math>. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> b0824115b52c8e198a2c16ebfab9998908663bd9 Somatório e Produtório 0 82 280 226 2015-12-09T03:59:27Z Jaimerson 27 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Provas de algumas propriedades == ====Multiplicação por constante==== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 6758de83f5ac7c47529d541c728463bbdae6e968 282 280 2015-12-09T04:33:33Z Jaimerson 27 /* Multiplicação por constante */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Provas de algumas propriedades == ====Multiplicação por constante==== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot F(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, \forall t \in N. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 18fd8f631346538f31ec95a17124a1d84cde3328 283 282 2015-12-09T04:34:24Z Jaimerson 27 /* Passo indutivo: s < t */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Provas de algumas propriedades == ====Multiplicação por constante==== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot F(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 9e54c7766bcdc2b8a148eb468374b40b1ff0dcc9 284 283 2015-12-09T04:34:44Z Jaimerson 27 /* Provas de algumas propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot F(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 88527bd0c0715958dea57b24d5e84498655281d2 285 284 2015-12-09T05:00:17Z Jaimerson 27 /* Provas de algumas propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> d2cb62bf56f97ae4c5de2ff2ee32e7467b40c8ef Contagem 0 84 312 311 2015-12-09T16:31:59Z Igorolivei 26 /* 5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados co... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. e983bcf21920fcde24f1ce3511697a3912d992c5 313 312 2015-12-09T16:32:12Z Igorolivei 26 /* 4. Nós queremos olhar para os coeficientes binomiais C(2n, n). Especificamente, para muitos exemplos, nós queremos determinar se C(2n, n) é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites e... wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== Solução Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. c697158fabe95c7af2c0f550c83aeb958a9b979c 314 313 2015-12-09T16:32:29Z Igorolivei 26 /* 3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos. */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. fb7d7eeb59978f4b23756bedc2038e0cbd377b91 315 314 2015-12-09T16:32:37Z Igorolivei 26 /* 3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos. */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== Solução A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 194f55ff0bbfae9b0e9afce5e0bab57c05f2dd96 316 315 2015-12-09T16:32:49Z Igorolivei 26 /* 2. Dados inteiros positivos n e r, liste todas as r-combinações, com repetições permitidas, do conjunto . */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== Solução Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 412b57b67776871d4dd236814f465d95e4bcc996 317 316 2015-12-09T16:33:03Z Igorolivei 26 /* 1. Dado um inteiro positivo n, encontre a probabilidade de selecionar seis inteiros do conjunto {1, \cdots , n} que foram mecanicamente selecionados em uma loteria. */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 5c49f70ddfc61a408827a51c07f539f40d319e7d 320 317 2015-12-09T19:04:44Z Patrickts 2 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== '''EXEMPLO (E1, pág 314)''' Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 0e607d55bf6c6d0ddcd477f92f979826a9c965ab 322 320 2015-12-09T19:53:59Z Patrickts 2 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== =====EXEMPLO (E1, pág 314)===== Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. [[Solução: EXEMPLO (E1, pág 314)]] '''EXEMPLO (E2, pág 314)''' Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. ef77d59c65c1939d1b36535a7f650e160d1374dc 325 322 2015-12-09T19:57:38Z Patrickts 2 /* EXEMPLO (E1, pág 314) */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 1 (pág 314)===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.''' [[Solução: Exemplo 1 (pág 314)]] ===== Exemplo 2 (pág 314)===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6.''' [[Solução: Exemplo 2 (pág 314)]] '''EXEMPLO (E3, página 314)''' Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard. '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. e40d9944c837a7a00e5a6704fbcbb1d7c0cb387d 326 325 2015-12-09T20:00:33Z Patrickts 2 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Contagem: Exemplo 1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6.''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. ae33dd0f6cd2d93aeb2717c55c829435e48268af 330 326 2015-12-09T20:09:10Z Patrickts 2 /* Contagem: Exemplo 2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Contagem: Exemplo 1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. e9d21444d16cf863b90125562b497d8c03ab5187 331 330 2015-12-09T20:09:55Z Patrickts 2 /* Contagem: Exemplo 1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Contagem: Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 3abbf046f15dd21a43b01df86ed7fe4441b61a9c 332 331 2015-12-09T20:10:44Z Patrickts 2 /* Contagem: Exemplo 4.2.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ==='''Exemplos extras da seção 4.1'''=== '''EXEMPLO (E1, pag 302)''' Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 2f8d1186fbcc98138117c3ea20d199ce6338423c 333 332 2015-12-09T20:13:49Z Patrickts 2 /* Exemplos extras da seção 4.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== '''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)''' [[Exemplo 4.1.1 - Solução]] '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 023de96076e2905687f5da84a961d0cf6f25294d 335 333 2015-12-09T20:14:43Z Patrickts 2 /* Exemplo 4.1.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== '''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)''' [[Exemplo 4.1.1 - Solução]] '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 4ae105d1d3bfc8b4b2a00ee160a8cd71268bf57f 336 335 2015-12-09T20:15:06Z Patrickts 2 /* Exemplo 4.1.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. a3e37ad39f0cb379471ab8b1b16546921d3352f0 338 336 2015-12-09T20:57:59Z Jeffersonwaa 11 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. afa9f44e0aa8c0b516e4e57bf429526068641353 343 338 2015-12-09T21:07:04Z Jeffersonwaa 11 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 6a70c7d6c4214c17f5c7d389f78f43229fb2c835 344 343 2015-12-09T21:10:19Z Jeffersonwaa 11 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 58851d794ee7be9e660e7cb84b7422e46f907cc9 347 344 2015-12-09T21:14:27Z Jeffersonwaa 11 wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] '''EXAMPLE (E2, pag 302)''' Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. '''EXAMPLE (E3, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . '''EXEMPLO (E4, page 302)''' Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. '''EXEMPLO (E5, pag 306)''' Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 '''EXAMPLE (E6, page 306)''' 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! '''EXEMPLO (E7, página 308)''' Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] ecc6653e0bf3af2d72e214dae260a408fa1e7b39 352 347 2015-12-09T21:33:06Z Jeffersonwaa 11 /* Exemplos Extras */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 971a4518cc446d6a3ed9f290e41cd9504d0d484b 355 352 2015-12-09T21:37:24Z Jeffersonwaa 11 /* Exemplos extras da seção 4.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressao executados é 2xn² . ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 07900d4c02decab116bfc5c6a068a92b65f54430 360 355 2015-12-09T21:41:53Z Jeffersonwaa 11 /* Exemplo 4.1.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' sao executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressoes é i + (n-i) = n. ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 2ce01df31886b30d4ea078c774b776cec8e43ff7 Somatório e Produtório 0 82 318 285 2015-12-09T17:34:24Z Jaimerson 27 /* Aplicação das Propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> a9fe1dc64acaa97b5c3c2796474aa6295546e0c1 319 318 2015-12-09T18:07:07Z Jaimerson 27 /* Aplicação das Propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> aacebb0fa69e4fde87ac368de00329461df3addc Solução: EXEMPLO (E4, página 345) 0 85 321 2015-12-09T19:05:14Z Patrickts 2 Created page with "'''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar..." wikitext text/x-wiki '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. 0b508c701e3f7a8f4c446dafbf32cc9a30475371 342 321 2015-12-09T21:05:35Z Jeffersonwaa 11 wikitext text/x-wiki '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. 0385e1fde6054c463036bd7ea2ddf9ed3ba43851 Solução: EXEMPLO (E1, pág 314) 0 86 323 2015-12-09T19:54:44Z Patrickts 2 Created page with " ===== Exemplo 1 (pág 314)===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.''' Considere dois compart..." wikitext text/x-wiki ===== Exemplo 1 (pág 314)===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. 49bb18ef107f9c159844d3830afe254f155bcb6a 324 323 2015-12-09T19:55:06Z Patrickts 2 /* Exemplo 1 (pág 314) */ wikitext text/x-wiki '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. 49b29c3966845a5c1a9af0d8e9c06e14cf315f45 Contagem: Exemplo 1 - Solução 0 87 327 2015-12-09T20:01:20Z Patrickts 2 Created page with " '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.''' Considere dois compartimentos, classificado em Par e Í..." wikitext text/x-wiki '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. 49b29c3966845a5c1a9af0d8e9c06e14cf315f45 Contagem: Exemplo 3 - Solução 0 88 328 2015-12-09T20:02:40Z Patrickts 2 Created page with "'''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndar..." wikitext text/x-wiki '''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)''' ''Solução:'' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). 155e32046de0696be5da617aa630cd6f089b3e0e Contagem: Exemplo 2 - Solução 0 89 329 2015-12-09T20:08:13Z Patrickts 2 Created page with "'''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6.''' ''S..." wikitext text/x-wiki '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6.''' ''Solução:'' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. 9b026cfc9987aca4cd41b03679d48be8776b059c Exemplo 4.1.1 - Solução 0 90 334 2015-12-09T20:14:08Z Patrickts 2 Created page with "'''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas m..." wikitext text/x-wiki '''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)''' ''Solução:'' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. ce8ec80fbc7c25dd066c14d6dd62bd65ab631a60 337 334 2015-12-09T20:15:38Z Patrickts 2 wikitext text/x-wiki ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. 41a61b49f7b846390a498194c3b6addddf0ce913 349 337 2015-12-09T21:19:49Z Patrickts 2 wikitext text/x-wiki ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. [[Contagem]] 2d1172d54313563068bca1a355073d1218378495 350 349 2015-12-09T21:21:55Z Patrickts 2 wikitext text/x-wiki ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. [[Category:Contagem]] 557633b9bd79895b137c7803d936199e776a79af 351 350 2015-12-09T21:24:22Z Patrickts 2 wikitext text/x-wiki ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. [[Contagem]] 2d1172d54313563068bca1a355073d1218378495 Solução: EXEMPLO (E2, página 345) 0 91 339 2015-12-09T21:00:19Z Jeffersonwaa 11 Created page with "Solução: Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes..." wikitext text/x-wiki Solução: Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. 8ed469be181180b89d7dbd399d622df85a13a156 346 339 2015-12-09T21:12:47Z Jeffersonwaa 11 wikitext text/x-wiki '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. 4bd89f3394600d1763e42d1a9aaddc0dfda092f9 Solução: EXEMPLO (E3, página 345) 0 92 340 2015-12-09T21:03:12Z Jeffersonwaa 11 Created page with "Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro..." wikitext text/x-wiki Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. a699744f4fa246289c0efbee6c8721e03bbda616 341 340 2015-12-09T21:03:47Z Jeffersonwaa 11 wikitext text/x-wiki Solução: Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. 8c3adfc84801a553e2014c25c72c561a911ff506 345 341 2015-12-09T21:11:45Z Jeffersonwaa 11 wikitext text/x-wiki '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. 734d378c033d3b727b0acd717970f50e8c20cb55 Solução: EXEMPLO (E5, página 345) 0 93 348 2015-12-09T21:14:39Z Jeffersonwaa 11 Created page with "'''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em po..." wikitext text/x-wiki '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. cb1c02d99a6493507e008acbc4ba1fdba78db9cd Exemplo 4.1.2 - Solução 0 94 353 2015-12-09T21:35:55Z Jeffersonwaa 11 Created page with "Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher ..." wikitext text/x-wiki Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. 1f9dc81cb362c8ea4e69ca1dd390046ceb29765b 354 353 2015-12-09T21:36:33Z Jeffersonwaa 11 wikitext text/x-wiki Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. 0f21fc5ca2e8247822925f9842a14b9aa651c3a2 356 354 2015-12-09T21:37:52Z Jeffersonwaa 11 wikitext text/x-wiki Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. 53b0b9b0fd84f53e00a4a8d01d00d578e68d9a50 357 356 2015-12-09T21:38:12Z Jeffersonwaa 11 wikitext text/x-wiki Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. [[contagem]] abe0210c88a06d1103c83c1228cf960f8cf8a204 Exemplo 4.1.3 - Solução 0 95 358 2015-12-09T21:40:25Z Jeffersonwaa 11 Created page with "Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim '''Solução:''' Para cada valor..." wikitext text/x-wiki Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' são executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressão executados é 2xn² . [[contagem]] 3503288265a3ffe6d05a77476d432430a6fafe41 359 358 2015-12-09T21:40:39Z Jeffersonwaa 11 wikitext text/x-wiki Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' são executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressão executados é 2xn² . [[contagem]] 6e64cd4cae90e6c80684ac22f0f352834c9ec2d1 Exemplo 4.1.4 - Solução 0 96 361 2015-12-09T21:43:41Z Jeffersonwaa 11 Created page with "Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim '''Solução:''' Para cada valo..." wikitext text/x-wiki Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' são executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressões é i + (n-i) = n. 46459560026f17e942833b0325599907377d29b1 Exemplo 4.1.4 - Solução 0 96 362 361 2015-12-09T21:44:16Z Jeffersonwaa 11 wikitext text/x-wiki Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' são executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressões é i + (n-i) = n. [[contagem]] 3e8c9f12c2818e5ec6b38fddda2b7291766cf53e Contagem 0 84 363 360 2015-12-09T21:44:44Z Jeffersonwaa 11 /* Exemplo 4.1.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] d5dfafa827a080606ae86b439036e37eab237e0f 365 363 2015-12-09T22:01:14Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 8a4e5e12cf89cf6c3364e4ebc41a2e4205f9381e 368 365 2015-12-09T22:02:28Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; <nowiki> C _ _ _ _ _ _ _ _ V</nowiki> Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; <nowiki> V _ _ _ _ _ _ _ _ C</nowiki> Logo,pela regra da soma : <nowiki> (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17)</nowiki> d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] e1a6500886878e0192b820ca6880ca09ea2005df 369 368 2015-12-09T22:04:09Z Jeffersonwaa 11 /* Exemplo 4.1.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 8e53fdf5db152a0ef983a919002e17cb905af924 375 369 2015-12-09T22:09:58Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[EXEMPLO (E1, página 345) - Solução]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 17c89b87abec3ccc51dde7282dd94ddd49e70195 376 375 2015-12-09T22:12:37Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: <nowiki> MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM.</nowiki> Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 8e53fdf5db152a0ef983a919002e17cb905af924 377 376 2015-12-09T22:14:28Z Jeffersonwaa 11 /* Exemplo 4.1.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.2 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] abfb7d456dbcd922d0497ec371af924a0f145eeb 379 377 2015-12-09T22:16:46Z Jeffersonwaa 11 /* Exemplo 4.1.7 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b)Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c)Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d)Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e)Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 06e425fce68f57477daf2e0aff249932e0a95c4a 382 379 2015-12-09T22:19:17Z Jeffersonwaa 11 /* Exemplo 4.1.7 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (pág 314)''' [[Contagem: Exemplo 1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 3cedfd9832571290a2721cf46cdb4a1e93d3ebe0 383 382 2015-12-09T22:23:37Z Jeffersonwaa 11 /* Exemplo 4.2.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Contagem: Exemplo 2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Contagem: Exemplo 3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 3ed119f9c317f2c3ce9c55477ab85a1ca053f788 385 383 2015-12-09T22:28:25Z Jeffersonwaa 11 /* Exemplos da Seção 4.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis? [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 3a13fc3f5e4a2ac13a46a0ebf03b21161650b213 386 385 2015-12-09T22:30:09Z Jeffersonwaa 11 /* Exemplo 4.1.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki> [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 976d30b935282d4957e175846817ae52b2bc0c83 387 386 2015-12-09T22:30:37Z Jeffersonwaa 11 /* Exemplo 4.1.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 5730a25aad5941c951e6c43572a2e772a4f46d69 388 387 2015-12-09T22:31:23Z Jeffersonwaa 11 /* Exemplo 4.1.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. (a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> ''de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello '' fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 1c03e02f51000533bf94c1d6798783662a9c7a25 389 388 2015-12-09T22:33:24Z Jeffersonwaa 11 /* Exemplo 4.1.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' (b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> ''de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello '' fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 198e26123bdfd308b55f98c67052246d602532d6 390 389 2015-12-09T22:33:59Z Jeffersonwaa 11 /* Exemplo 4.1.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> ''de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello '' fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 40069b4796cab3bb03d4ef83e51b30dcd55b3492 391 390 2015-12-09T22:34:26Z Jeffersonwaa 11 /* Exemplo 4.1.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki>'' ''de i=1 até n'' ''inicio'' ''de j=1 ate n'' print hello de k=1 ate n print hello '' fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 0432d9d05743efcd754ceee3e838242f2547b9ad 392 391 2015-12-09T22:35:04Z Jeffersonwaa 11 /* Exemplo 4.1.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki>'' de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] f7732702de820eb9cee7e81dc0393390f46c3c04 393 392 2015-12-09T22:35:21Z Jeffersonwaa 11 /* Exemplo 4.1.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki> [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] d23c6bbed9bbfa2885f9b473d848ae966da6cb48 394 393 2015-12-09T22:36:06Z Jeffersonwaa 11 /* Exemplo 4.1.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 853d611acadb1217c218b9c1f3e02dfd53a415ff 395 394 2015-12-09T22:36:33Z Jeffersonwaa 11 /* Exemplo 4.1.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições.'' [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] f845560da32c3ab7daa8d0f3565a0a3d2c7c219f 396 395 2015-12-09T22:36:45Z Jeffersonwaa 11 /* Exemplo 4.1.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições.'' [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] bb7c33f640af78f35b28194713df7e060e712840 397 396 2015-12-09T22:37:02Z Jeffersonwaa 11 /* Exemplo 4.1.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] d021ec58bde75871d7be781e2a55c2bfdf6c425f 398 397 2015-12-09T22:37:33Z Jeffersonwaa 11 /* Exemplo 4.1.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] d7d1c481de9cd9e94f73de43f0b166d72516da6b 399 398 2015-12-09T22:38:14Z Jeffersonwaa 11 /* Exemplo 4.1.7 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== '''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)''' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] f9cebb91bb17881e47f262ae379bcafecaf4a3ad 400 399 2015-12-09T22:39:11Z Jeffersonwaa 11 /* Exemplo 4.2.1 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== '''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)''' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] ef9652b5ed5db7da7f1e646277a48d0d58574d7c 401 400 2015-12-09T22:40:13Z Jeffersonwaa 11 /* Exemplo 4.2.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Contagem: Exemplo 2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 94eb8688960f575eba97c54153bcb0fbb6054d0f 402 401 2015-12-09T22:41:05Z Jeffersonwaa 11 /* Exemplos da Seção 4.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Contagem: Exemplo 3 - Solução]] '''EXEMPLO (E4, página 315)''' Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas. '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. '''EXEMPLO (E5, página 315)''' Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe. '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 14db59a9e81092cc6f50f52f0de20d97c791b5be 406 402 2015-12-09T22:44:59Z Jeffersonwaa 11 /* Exemplos da Seção 4.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314) [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315) '''Solução:''' O numero de codigos possiveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600,pelo menos 4 tenham o mesmo codigo. ===== Exemplo 4.2.5 ===== Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315) '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe.Mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe.Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O numero minimo é 29. ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] b6a6760a032444b655c1bc66906d4383d121ac71 408 406 2015-12-09T22:48:18Z Jeffersonwaa 11 /* Exemplos da Seção 4.2 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 6201f6e7c33c18aa492c6106ea1cbbcc7eb1b4f4 Exemplo 4.1.5 - Solução 0 97 364 2015-12-09T21:46:34Z Jeffersonwaa 11 Created page with "Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem)..." wikitext text/x-wiki Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 [[contagem]] 4dfa66cb355f59653cec085dcf27e38d3e8f6a79 370 364 2015-12-09T22:04:35Z Jeffersonwaa 11 wikitext text/x-wiki Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b)Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c)Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 [[contagem]] 3761e5614cee34329fbb27404e2d82838210a931 371 370 2015-12-09T22:05:15Z Jeffersonwaa 11 wikitext text/x-wiki Encontre o numero de palavras com 10 letras sem repeti-las: (a) que não tenha vogais. (b) que começam com uma vogal. (c) que tenha C e V nas extremidades (em qualquer ordem). (d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b) Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c) Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 [[contagem]] cc65fbfc71e97393f78f9911d5d39b2359d31900 Solução: EXEMPLO (E1, página 345) 0 98 366 2015-12-09T22:01:31Z Jeffersonwaa 11 Created page with "'''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math>" wikitext text/x-wiki '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> 15e56b510d58c1f161934bbfd54e2e0c8ca3fa15 367 366 2015-12-09T22:01:54Z Jeffersonwaa 11 wikitext text/x-wiki '''Solução:''' Procedendo do menor ao maior, as permutações são: <math>324165, 324615, 326145, 461235, 461325, 462135, 516243</math> [[contagem]] c82222ff9b0f46e5591da124097535cbe4180ec5 Exemplo 4.1.6 - Solução 0 99 372 2015-12-09T22:07:42Z Jeffersonwaa 11 Created page with "10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas ..." wikitext text/x-wiki 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! [edit] 0c3db17ebc0a7c65b984695cacb9ac6fb42b04b5 373 372 2015-12-09T22:08:06Z Jeffersonwaa 11 wikitext text/x-wiki 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! [[contagem]] a2cbc674ad36fb1527bd643c383719c4f13eddc5 374 373 2015-12-09T22:08:37Z Jeffersonwaa 11 wikitext text/x-wiki 10 homens e 10 mulheres estão em uma fila: (a) encontre quantas possibilidades pode ser formada a fila. (b) encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a)Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b)se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c)Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! [[contagem]] 2482a7dde1d65a5c98136fa3ee022e98939e142e 378 374 2015-12-09T22:15:14Z Jeffersonwaa 11 wikitext text/x-wiki 10 homens e 10 mulheres estão em uma fila: (a) Encontre quantas possibilidades pode ser formada a fila. (b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; (c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). '''Solução:''' a) Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b) Se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c) Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! [[contagem]] 9c80941a0872ba00febdc48e3c4eaf882c872aa3 Exemplo 4.1.7 - Solução 0 100 380 2015-12-09T22:18:15Z Jeffersonwaa 11 Created page with "Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e termina..." wikitext text/x-wiki Encontre o número de palavras 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b) Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c) Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d) Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e) Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); [[contagem]] a35b76173a3e5a5ca35b2a171527ab9452e7dabc 381 380 2015-12-09T22:18:48Z Jeffersonwaa 11 wikitext text/x-wiki Encontre o número de palavras com 10 letras : (a) não contenha vogais. (b) começar com uma vogal. (c) ter vogais nas duas primeiras posições. (d) começar com C e terminam com V. (e) começar com C ou terminar com V. Para resolver o problema é ter em mente uma fila de dez espaços em branco : '''Solução:''' a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,como podemos repeti-las.Pela regra do produto: 21 X 21 X 21 X 21 X ... X 21 = 21^10 ; b) Há cinco opções para uma vogal ser colocada na primeira posição, e não há restrições sobre os outros nove letras,por isso : 5 x 26^9 c) Se essas vogais devem estar nas duas primeiras posições e as letras podem ser repetidas, obtém-se o produto: 5² x 26^8 d) Se a palavra tem a forma : C....V existem 26 maneiras para preencher cada uma dos oito espaços. Portanto, há 26^8 palavras desta forma. e) Precisa-se usar o princípio da inclusão-exclusão para evitar a dupla contagem.Sendo A¹ o conjunto de todas as palavras com 10 letras que começam com C e A² o conjunto de todas as palavras com 10 letras que terminam com V: A¹ U A² = |A¹|+|A²| - |A¹ n A²| = 26^9 + 26^9 - (26^8); [[contagem]] bb668d7dd54938ff1c7d91b29c1189312b1b844c Exemplo 4.2.1 - Solução 0 101 384 2015-12-09T22:24:50Z Jeffersonwaa 11 Created page with "Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado ..." wikitext text/x-wiki Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. [[contagem]] c57c8b1383d6ea47738f4192d7f56cfac45304b3 409 384 2015-12-09T22:48:46Z Jeffersonwaa 11 wikitext text/x-wiki ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par.'' '''Solução:''' Considere dois compartimentos, classificado em Par e Ímpar. Se três inteiros positivos são colocados nestes compartimentos, um deles deve ter pelo menos dois inteiros (digamos A e B) no mesmo compartimento. Assim, A e B são ou ambos par ou impar. Em ambos os casos, A + B é PAR. [[contagem]] f3c22b2466ecc58d36eb504b5ee3afe7dce68496 Exemplo 4.2.2 - Solução 0 102 403 2015-12-09T22:42:06Z Jeffersonwaa 11 Created page with "''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6.'' '''Sol..." wikitext text/x-wiki ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6.'' '''Solução:''' Para que A e B serem congruentes módulo 6, temos de ter a mod 6 = b mod 6. Mas existem 6 possibilidades para x mod 6: 0, 1, 2, 3, 4, ou 5. Portanto, 7 inteiros positivos devem ser escolhidos de modo a garantir que, pelo menos, dois sejam congruentes módulo 6. [[contagem]] dd0e0eaded43e6de96f270f5fc2de5ed2934f0f0 Somatório e Produtório 0 82 404 319 2015-12-09T22:42:49Z Francleidepsimao 22 /* Aplicação das Propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n-1} k = 1+2+...+n-1= \frac{n \cdot (n-1)}{2}</math> média: <math>\frac {\frac{n \cdot (n-1)}{2}}{n-1} = 16,1</math> <math>\frac{n \cdot (n-1)}{2(n-1)} = 16,1</math> <math>n \cdot \cancel{(n-1)} = 16,1\cdot 2\cancel{(n-1)} </math> <math>n = 32,2</math> ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> fd49a42b84dc4c0419e2e1b66233e8eb2e782e4a 405 404 2015-12-09T22:43:26Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n-1} k = 1+2+...+n-1= \frac{n \cdot (n-1)}{2}</math> média: <math>\frac {\frac{n \cdot (n-1)}{2}}{n-1} = 16,1</math> <math>\frac{n \cdot (n-1)}{2(n-1)} = 16,1</math> <math>n \cdot \cancel{(n-1)} = 16,1\cdot 2\cancel{(n-1)} </math> <math>n = 32,2</math> em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> d5adb87effac33c733b607b90fb935a1b9856f06 Exemplo 4.2.3 - Solução 0 103 407 2015-12-09T22:45:41Z Jeffersonwaa 11 Created page with "''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard...." wikitext text/x-wiki ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' '''Solução:''' O número de possíveis pares de letras que podem aparecer nas duas primeiras posições é 26 x 26=676.Assim, qualquer conjunto de 677 ou mais palavras deve ter pelo menos duas palavras com o mesmo par de letras no início da palavra. (OBS:. Na realidade, o número 700 pode ser substituída com um número muito menor, uma vez que muitas combinações de letras não aparecem como as duas primeiras letras de uma palavra, por exemplo, não há palavras inglesas que começam com NQ, RR, ou TZ). [[contagem]] 53358037b0d9bf86a2d3106a2880953b8b32c22d Exemplo 4.2.4 - Solução 0 104 410 2015-12-09T22:50:06Z Jeffersonwaa 11 Created page with "''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, s..." wikitext text/x-wiki ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' '''Solução:''' O número de códigos possíveis 26 x 10 x 10 = 2600. Desde que,8000 > 3 x 2600, pelo menos 4 tenham o mesmo código. [[contagem]] e90a662c79e62dd3ccc9dbbec1aeaf697cc92200 411 410 2015-12-09T22:50:20Z Jeffersonwaa 11 wikitext text/x-wiki ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' '''Solução:''' O número de códigos possíveis 26 x 10 x 10 = 2600. Desde que, 8000 > 3 x 2600, pelo menos 4 tenham o mesmo código. [[contagem]] c143b0b7e068286337578aaf2f7db471d6a6b6c4 Exemplo 4.2.5 - Solução 0 105 412 2015-12-09T22:51:37Z Jeffersonwaa 11 Created page with "''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhid..." wikitext text/x-wiki ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe, mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe. Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O número mínimo é 29. [[contagem]] 89ecb96c915ed0dab8a2e7a71df76659929aec47 413 412 2015-12-09T22:51:56Z Jeffersonwaa 11 wikitext text/x-wiki ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' '''Solução:''' De um grupo de 28 estudantes podem ser 7 pertencentes a cada classe, mas se há 29 estudantes, pelo menos 8 devem ser membros da mesma classe. Portanto, o número mínimo de estudantes que deve ser escolhido é de 29. Em outras palavras, nós estamos olhando para o número mínimo N tal que <math>|\frac{N}{4} | = 8</math>. O número mínimo é 29. [[contagem]] 1242e75ac6118e8335aafd64cca0cb518a66c659 Exemplo 4.1.2 - Solução 0 94 414 357 2015-12-09T22:53:32Z Jeffersonwaa 11 wikitext text/x-wiki ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5. ''(a) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, com números repetidos permitidos, quantos códigos de entrada são possíveis? ''(b) Se você escolher um código de entrada que consiste de uma sequencia de 4 dígitos, sem repetir os números, quantos códigos de entrada são possíveis? '''Solução:''' (a) Precisa-se preencher os espaços em branco, e cada espaço pode ser preenchido com qualquer um dos 5 dígitos 1,2,3,4,5. Pela regra do produto geral, resolvemos com 5^4=625 maneiras. (b) Precisa-se preencher os espaços em branco,mas cada espaço deve ser preenchido com inteiros diferentes de 1 a 5.Usando a regra do produto pode ser aplicado 5! = 5x4x3x2 = 120 maneiras. [[contagem]] 6fa99f10141fa9d432d9167f5539cab09ec06d34 Exemplo 4.1.3 - Solução 0 95 415 359 2015-12-09T22:53:56Z Jeffersonwaa 11 wikitext text/x-wiki ''Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' são executados. Assim a cada i, o número de declarações de impressão executado é 2Xn .Portanto o numero total de instruções de impressão executados é 2xn² . [[contagem]] 682455d96aeeed32e787c7cb961511d195fdffc4 Exemplo 4.1.4 - Solução 0 96 416 362 2015-12-09T22:54:30Z Jeffersonwaa 11 wikitext text/x-wiki ''Conte os números de instruções de impressão nesse algoritmo: de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim '''Solução:''' Para cada valor de i,tanto o laço do 'j' como o do 'k' são executados. Assim a cada laço do i, o número de declarações de impressão executado é i no primeiro laço mais n-i no segundo laço. Portanto para cada i, o numero de impressões é i + (n-i) = n. [[contagem]] 13a53d4cabb022f60d0f940f8bdfb4c46224c203 Exemplo 4.1.5 - Solução 0 97 417 371 2015-12-09T22:55:04Z Jeffersonwaa 11 wikitext text/x-wiki ''Encontre o numero de palavras com 10 letras sem repeti-las: ''(a) que não tenha vogais. ''(b) que começam com uma vogal. ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b) Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c) Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 [[contagem]] 2bc9b2be6eaa6df2e6456dd94900da84ef01b165 418 417 2015-12-09T22:55:15Z Jeffersonwaa 11 wikitext text/x-wiki ''Encontre o numero de palavras com 10 letras sem repeti-las: ''(a) que não tenha vogais. ''(b) que começam com uma vogal. ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. '''Solução:''' Para resolver o problema é ter em mente uma fila de dez espaços em branco : a) Cada um dos 10 espaços em branco da cadeia deve conter 1 das 21 consoantes,sem repeti-las.Pela regra do produto: 21 X 20 X 19 X 18 X ... X 12. b) Existem 5 possibilidades da primeira letra ser uma vogal.Se a vogal for colocada no primeiro espaço em branco existem 25 maneiras para preencher no segundo espaço,24 maneiras de preencher o terceiro espaço,etc . 5 x 25 x 24 x 23 x ... x 18 x 17. c) Primeiramente contamos o número de maneiras de preencher os 10 espaços começando com C e terminando com V,o numero de manerias de preencher as oito letras restantes é 24 x 23 x ... x 18 x 17; C _ _ _ _ _ _ _ _ V Da mesma forma,o número de palavras,porem agora,começando com V e terminado com C, 24 x 23 x ... x 18 x 17; V _ _ _ _ _ _ _ _ C Logo,pela regra da soma : (24 x 23 x ... x 18 x 17) + (24 x 23 x ... x 18 x 17) = = 2 x (24 x 23 x ... x 18 x 17) d) Primeiramente vamos contar o número de maneiras de colocar as vogais nos dois primeiros espaços em branco.Podemos escolher qualquer uma das 5 vogais para a primeiro espaço e das 4 vogais restantes para o 2 espaço : 5 x 4=20 maneiras de colocar duas vogais nas duas primeiras posições. Em seguida, vamos preencher os 8 espaços restantes com 24 letras que faltam.Sendo feito da seguinte forma : 24 x 23 x ... x 18 x 17 maneiras. Portanto, o número de maneiras de colocar vogais nois dois primeiros espaços e oito letras nos restantes dos espaços é: 5 x 25 x 24 x 23 x ... x 18 x 17 [[contagem]] e54f77dae7e140197342050ffb77b525b4385414 Exemplo 4.1.6 - Solução 0 99 419 378 2015-12-09T22:55:49Z Jeffersonwaa 11 wikitext text/x-wiki ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ''ou Darryl, Beryl, e Carol). '''Solução:''' a) Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b) Se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c) Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! [[contagem]] 40a2b30816177d3e338dd93264c6f80019482946 420 419 2015-12-09T22:56:09Z Jeffersonwaa 11 wikitext text/x-wiki ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol).'' '''Solução:''' a) Há 20 pessoas;Portanto eles podem ser colocados em uma fila: 20 x 19 x 18 x....x 1 = 20! b) Se duas pessoas do mesmo sexo não podem ficar lado a lado;Entao há dois padroes possiveis, usando M para Masulino e F para Feminino: MFMFMFMFMFMFMFMFMFMF e FMFMFMFMFMFMFMFMFMFM. Se contar o numero de maneiras de se obter a primeira possibilidade, dobramos ela para chegarmos ao resultado final.O Primeiro homem pode ser escolhido em 10 maneiras, a Primeira mulher pode ser escolhida de 10 Maneiras, o homem Segundo pode ser escolhido de 9 maneiras, etc.Assim,pela regra do produto temos : 10 x 10 x 9 x 9 x ... x 2 x 2 x 1 x 1 ou (10!)² maneiras. c) Considerando primeiro os arranjos onde Beryl,Carol e Darryl ficam um ao lado do outro,nessa ordem.Colocando as outras 17 pessoas na fileira.o que pode ser feito em 17! Maneiras.Nao importa como as 17 pessoas sao colocadas na fila,Beryl,Carol e Darryl,pode ser inserido,nessa ordem,entre duas das 17, ou então colocado em uma das duas extremidades. No entanto, uma vez que escolher um local para colocar Beryl, Carol, e Darryl, existem 3! = 6 maneiras de colocar Beryl, Carol, e Darryl nesse ponto --- BCD, BDC, CBD, CDB, DBC, DCB. Portanto, a resposta é obtida colocando os outros 17 em uma fileira; escolher um dos 18 pontos para Beryl, Carol, e Darryl; e organizar os três em um local (em 3! maneiras). Assim, a resposta é: 17! x 3! [[contagem]] b172da13698dea3e7de1556802775516a4fa8ab1 Contagem 0 84 421 408 2015-12-09T22:59:05Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 82f2fef8951c002973a8d523808beb9c0e9c53e1 422 421 2015-12-09T22:59:32Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] dd752e87daa6e8c9cc2ee0a42079736fbad2f9e5 423 422 2015-12-09T23:00:19Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] e6a24257575f00629ae98e5b47baca3816444166 424 423 2015-12-09T23:02:34Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 7fdd181a30cca64625062d94302871b46df599c0 425 424 2015-12-09T23:02:57Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] cfe706ba1f4d8e33b470e3dea867cd310df93be6 426 425 2015-12-09T23:03:39Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] 3f39c0711039454223090519f1cee69201270ccd 427 426 2015-12-09T23:04:13Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] a743432cac0efb9305dd74e3ff01d321a393c88c 428 427 2015-12-09T23:04:45Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? [[Solução: EXEMPLO (E5, página 345)]] c56004dfa13430778d50e672f8d527500e683256 429 428 2015-12-09T23:05:25Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 [[Solução: EXEMPLO (E1, página 345)]] '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. [[Solução: EXEMPLO (E2, página 345)]] '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. [[Solução: EXEMPLO (E3, página 345)]] '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. [[Solução: EXEMPLO (E4, página 345)]] '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. [[Solução: EXEMPLO (E5, página 345)]] 787beaa0fd14119b60b4449c021abcbd225a0f30 430 429 2015-12-09T23:06:17Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. ec421d7833126ef3394cc565af944e0a99a8cc0a 432 430 2015-12-09T23:14:14Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. EXEMPLO (E3, página 324) ….. '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 9c983c82861391ea0b677750197f1f5da2040da8 433 432 2015-12-09T23:14:43Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''EXEMPLO (E3, página 324)''' ... '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 6836c866fa2981d6169dc56687d4eeaa5d999cc5 434 433 2015-12-09T23:14:59Z Jeffersonwaa 11 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO (E1, pág 321)''' Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (a) Colocar 4 alunos em uma fila para uma foto? (b) Colocar todos os 30 alunos em uma fila para uma foto? (c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto? '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''EXEMPLO (E3, página 324)''' ... '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. a3712efcdc9f1fef2774d507b9f5550d4823d9e9 444 434 2015-12-10T02:13:14Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''EXEMPLO 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''EXEMPLO (E2, página 324)''' Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem? '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''EXEMPLO (E3, página 324)''' ... '''EXAMPLE (E4, page 324)''' Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens? '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''EXEMPLO (E5, page 324)''' Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares. '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''EXEMPLO (E6, page 324)''' Encontre maneiras de dividir um baralho de 52 cartas, em: a)Em 4 pilhas iguais, classificado em A,B,C,D; b)Em 4 pilhas iguais, sem classificação; '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''EXEMPLO (E7, page 324)''' Supunha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T: a) consista de 2 numeros impares e 3 numeros pares. b) consiste de exatamente três números primos. c) tenha a soma dos seus elementos, menor que 20. d) tem, pelo menos, um número par na mesma. '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 9e9307494b5c50309566c0cbf52690f37693c411 445 444 2015-12-10T02:16:30Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''EXEMPLO (E1, página 328)''' Escreva a expansão de (x+2y)³. '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''EXEMPLO (E2, page 328)''' Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''EXEMPLO (E3, page 328)''' Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math> '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. bd9f4edbc1cf8a2ba17d51f2c2b5f5930b2061e4 446 445 2015-12-10T02:24:53Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''EXEMPLO (E1, page 338)''' Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim c) No máximo 5 biscoitos de açúcar d) Pelo menos um dos quatro tipos de biscoitos. Solução: '''EXEMPLO (E2, page 339)''' Quantos anagramas podem ser formados pela palavra DECEIVED? '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''EXEMPLO (E3, page 339)''' Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. (b) Encontre o número de possíveis ‘punhados’ de 12 moedas. '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''EXEMPLO (E4, page 339)''' De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 0f55fac5121af39f3dd6460a6526533e7843062e 447 446 2015-12-10T02:31:20Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 4ac2cd5b816c4932a6bc7e05c6f63b47da1d94f8 448 447 2015-12-10T02:32:17Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. eed536c3a0cff63c44a7030948d89fa026d1950f 450 448 2015-12-10T02:34:55Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''EXEMPLO (E1, página 345)''' Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''EXEMPLO (E2, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''EXEMPLO (E3, página 345)''' Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''EXEMPLO (E4, página 345)''' Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''EXEMPLO (E5, página 345)''' Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. ac323e6cb96c8cd8489ef2aecbb298b02bd56406 451 450 2015-12-10T02:38:23Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. d55656649a10547c898638e10f8b5c02610e6134 452 451 2015-12-10T02:38:48Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 5a46d1c32a9ea4627436500217a4220ee2db2ee3 453 452 2015-12-10T02:57:25Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo - Solução]] '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo - Solução]] '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo - Solução]] '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo - Solução]] '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo - Solução]] '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 85031512ef2f77ce4e3d4196fc3cc398fd7c9618 457 453 2015-12-10T03:13:42Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 388b8ee572d498ef9b800eb840827e68454c26d3 Exemplo 4.1.1 - Solução 0 90 431 351 2015-12-09T23:13:06Z Jeffersonwaa 11 wikitext text/x-wiki ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' '''Solução:''' Uma vez que existe 3 maneiras para fazer a primeira parte da viajem e 5 maneiras de continuar com a segunda parte da viagem, independentemente de qual vôo for feita para a primeira etapa da viagem, pela regra do produto há 3 x 5 =15 maneiras de fazer toda a viagem. [[Contagem]] 84ebcb5bf365b5e5b764fe5a31e6cb22af2aaf83 Somatório e Produtório 0 82 435 405 2015-12-09T23:22:04Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n}</math> em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> edd833b7659724c8fd71b59e3d6069943540c363 436 435 2015-12-09T23:35:06Z Francleidepsimao 22 /* Aplicação das Propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n}</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 4089a371c0de58534292d3c058717cc00675c29a 437 436 2015-12-09T23:41:47Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 7c74f67aba906cdda322435db04bc306797bee36 438 437 2015-12-10T00:55:08Z Francleidepsimao 22 /* Aplicação das Propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5} </math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Pela propriedade da média aritmética uma sequencia (a, b e c) pertence a mesma progressão se <math>\frac {a+c}{2}= b </math> em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 34045afb6ed1952fa21e1688f1a97154cec5b3fa 439 438 2015-12-10T01:24:55Z Francleidepsimao 22 /* Aplicação das Propriedades */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Pela propriedade da progressão aritmética a média aritmética uma sequencia (a, b e c) pertence a mesma progressão se <math>\frac {a+c}{2}= b </math> Somatório com <math>\sqrt{k}</math>: <math>\sum_{k=1}^{n} \sqrt{k} = \sqrt{1} + \sqrt{2}+...+\sqrt{n}</math> Assumindo <math>n = 5</math> <math>\sum_{k=1}^{5} \sqrt{k} = \sqrt{1} + \sqrt{2}+\sqrt{3}+\sqrt{4}+\sqrt{5}</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> c21cb270b1805797c05d25902cacba50538f40d5 440 439 2015-12-10T01:26:46Z Francleidepsimao 22 /* Exemplo 7 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Pela propriedade da progressão aritmética a média aritmética uma sequencia (a, b e c) pertence a mesma progressão se <math>\frac {a+c}{2}= b </math> Somatório com <math>\sqrt{k}</math> expandido <math>n</math> vezes: <math>\sum_{k=1}^{n} \sqrt{k} = \sqrt{1} + \sqrt{2}+...+\sqrt{n}</math> Assumindo <math>n = 5</math> <math>\sum_{k=1}^{5} \sqrt{k} = \sqrt{1} + \sqrt{2}+\sqrt{3}+\sqrt{4}+\sqrt{5}</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. em processo de alteração... ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 2bdd9fa28191c7a05b0fafecd4ee73e877dfe4b6 441 440 2015-12-10T01:27:32Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Pela propriedade da progressão aritmética a média aritmética uma sequencia (a, b e c) pertence a mesma progressão se <math>\frac {a+c}{2}= b </math> Somatório com <math>\sqrt{k}</math> expandido <math>n</math> vezes: <math>\sum_{k=1}^{n} \sqrt{k} = \sqrt{1} + \sqrt{2}+...+\sqrt{n}</math> Assumindo <math>n = 5</math> <math>\sum_{k=1}^{5} \sqrt{k} = \sqrt{1} + \sqrt{2}+\sqrt{3}+\sqrt{4}+\sqrt{5}</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 30021d974462e9359e2069c0175e35c7e7ecb8a8 442 441 2015-12-10T01:37:01Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Pela propriedade da progressão aritmética a média aritmética uma sequencia (a, b e c) pertence a mesma progressão se <math>\frac {a+c}{2}= b </math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 648158f43066136fc1d2db0aa924adb98f25f83e 443 442 2015-12-10T02:06:49Z Francleidepsimao 22 /* Exemplo 7 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {1, 2, . . . , n}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 3d8032e9ec41f2f3ad398ea4c67263d9893d0830 449 443 2015-12-10T02:33:15Z Francleidepsimao 22 /* Exemplo 4 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> fa617fbd95a87cb91f17223ee86c572da7eb55b1 454 449 2015-12-10T02:57:53Z Francleidepsimao 22 /* Exemplo 6 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 81fe3706e702a3059156975c8eb4b3bbaa9b0fbc 456 454 2015-12-10T03:05:47Z Francleidepsimao 22 /* Exemplo 6 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde n ∈ N, com n ≥ 1. ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <math>1+\sum_{k=1}^{n} (k+1)!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 9ee7d7baed36c4cf3aaecd7037acf996c0e0358d Exemplo 4.3.1 - Solução 0 106 455 2015-12-10T02:59:11Z Igorolivei 26 Created page with "'''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de..." wikitext text/x-wiki '''Solução:''' (a) Precisamos preencher a seguinte linha de quatro espaços em branco: 30 x 29 x 28 x 27. Este é o número de permutações de 4 a partir de um conjunto de 30, que é P( 30 ,4 ); (b)A resposta pode ser visualizado como o número de maneiras para preencher uma fila com 30 lacunas com os 30 estudantes, que é 30! , ou P( 30, 30 ); (c) Podemos ver que o número de maneiras para preencher em duas filas,é cada uma com 15 espaços em branco, com os alunos 30: Podemos então, começar por preencher a linha de inferior, o que pode ser feito de 30 x 29 x 28 x … x 17 x 16 maneiras. Em seguida, preencher linha superior, que pode ser feito de 15! = 15 x 14 x 13… x 2 x 1 maneiras. Portanto a resposta é (30 x 29 x 28 x … x 17 x 16) x (15 x 14 x 13 x … x 2 x 1) = 30! cbe5abdb67864e949bfee35a6ddc11187bcfffc8 Exemplo 4.3.2 - Solução 0 107 458 2015-12-10T03:14:10Z Igorolivei 26 Created page with "'''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis." wikitext text/x-wiki '''Solução:''' Há 5⁴=625 possíveis códigos com quatro dígitos. Portanto, há C(625,6) conjuntos diferentes de códigos reconhecíveis. 940b9a5f122c04ce8703eda5446891367d875495 Exemplo 4.3.4 - Solução 0 108 459 2015-12-10T03:14:46Z Igorolivei 26 Created page with "'''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher t..." wikitext text/x-wiki '''Solução:''' O número de maneiras de escolher três mulheres é C( 10,3 ) e o numero de maneiras de escolher 10 homens é C(7,2).Usando a regra do produto para escolher três mulheres e dois homens é C( 10,3 ) x C(7,2) = 2,520. 3d440d44aa2c6b6b859871b7ab85f691a7e9a7bf Exemplo 4.3.5 - Solução 0 109 460 2015-12-10T03:14:58Z Igorolivei 26 Created page with "'''Solução:'''" wikitext text/x-wiki '''Solução:''' cc7042bdaf49d68969959d29537e8d4d48049d9d 461 460 2015-12-10T03:15:22Z Igorolivei 26 wikitext text/x-wiki '''Solução:''' Note que, existem 10 inteiros ímpares e 9 inteiros pares em S. Os subconjuntos a serem contados deve consistir de k inteiros ímpares e k inteiros pares, onde k=1,2,3,...,9. Portanto, pela regra do produto, o número de cada tipo é C(10, k) x C(9,k). Portanto, pela regra da soma, a resposta é C(10, k) x C(9,k) + C(10, k) x C(9,k) cda9e220322b2f0b47f0108b46e15bd3bc898883 Exemplo 4.3.6 - Solução 0 110 462 2015-12-10T03:15:40Z Igorolivei 26 Created page with "'''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a ..." wikitext text/x-wiki '''Solução:''' a) Cada pilha deve conter 52/4 = 13 cartas. Na sequencia, empilharemos A,em seguida B, depois C, e finalmente D. Então teremos C(52,13) maneiras de obter a pilha de A, C(39,13) maneiras de obter a pilha de B, C(26,13) maneiras de obter a pilha de C, e C(13,13)=1 maneiras de obter a pilha de D.Portanto pela regra do produto,teremos : C(52,13) x C(39,13) x C(26,13) x C(13,13) = <math>\frac{52!}{13!.29!} .\frac{39!}{13!.26!} .\frac{26!}{13!.13!} .\frac{13!}{13!.0!} = \frac{52!}{(13!)^4} </math> b) Se nas 4 pilhas não houver classificação,então podemos permutar as quatro pilhas em 4! Maneiras. Daí a resposta é a mesma do iten anterior dividido por 4!: <math>\frac{C(52,13).C(39,13).C(26,13).C(13,13)}{4!} = \frac{52!}{(13!)^4.4!}</math> 5a87a57bcf24388197681de887403e4d6f550202 Exemplo 4.3.7 - Solução 0 111 463 2015-12-10T03:15:53Z Igorolivei 26 Created page with "'''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto ..." wikitext text/x-wiki '''Solução:''' a) Há 13 numeros impares; podemos escolher dois em C(13,2) maneiras.Há 12 numeros pares; podemos escolher 3 em C(12,3) maneiras. Usando a regra do produto para encontrar o número de subconjuntos T, temos subconjuntos. b) Os numeros primos em S são 2,3,5,7,11,13,17,19, and 23, então temos C(9,3) maneiras de selecionar 3 desses numeros.Mas também precisa selecionar 2 dos 16 números compostos para fazer T ter tamanho cinco;então C(16,2) maneiras para isso.Portanto pela regra do produto temos C(9,3) x C(16,2)=10.080 subconjuntos possiveis T. c) Há poucos subconjuntos com esta propriedade. Então é melhor neste caso, contar diretamente o conjunto de cinco números cuja soma é inferior a 20: 1,2,3,4,5, 1,2,3,4,6, 1,2,3,4,7, 1,2,3,4,8, 1,2,3,4,9, 1,3,4,5,6. Assim, existem seis desses subconjuntos possiveis. d) É mais fácil para contar o número total de subconjuntos de tamanho 5, e depois subtrair o número de subconjuntos sem números pares neles: <math>C(25, 5)-C(13,5) = 51,843</math> 32a8b71ed9334fffae95d131ad1e4097b6621821 Contagem 0 84 464 457 2015-12-10T03:16:09Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. b69c0aae3fa15a1ac8fb3d03ca333678bc7e7b90 465 464 2015-12-10T03:18:21Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Solução:''' pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 7e832f6675c300a4bed225e7251d4fd40cf378c2 469 465 2015-12-10T03:21:48Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.4 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 704c3b60eb0be6e38e23c4974262e9ccec17c216 470 469 2015-12-10T03:25:35Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] Solução: '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 5ed721d3e808b24661993c90302fc425dd495650 475 470 2015-12-10T03:27:32Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 45042858b69cd0847b8f321501a9d37c5f8095f3 476 475 2015-12-10T03:31:17Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.5 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. b0cfe75ae9e4193171abcfb670a5c00d43286377 477 476 2015-12-10T03:31:52Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Exemplo 4.6. - Solução]] '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6. - Solução]] '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6. - Solução]] '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' [[Exemplo 4.6. - Solução]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' [[Exemplo 4.6. - Solução]] '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 2a4b098b1697740359fd8ae29d68dcb3dc189146 478 477 2015-12-10T03:32:07Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Exemplo 4.6.1 - Solução]] '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6.2 - Solução]] '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6.3 - Solução]] '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' [[Exemplo 4.6.4 - Solução]] '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' [[Exemplo 4.6.5 - Solução]] '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. 6103444cf16649569b39cda1b240cbddeb6fc0d1 484 478 2015-12-10T03:34:35Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.6 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativas a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Exemplo 4.6.1 - Solução]] '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6.2 - Solução]] '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6.3 - Solução]] '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' [[Exemplo 4.6.4 - Solução]] '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' [[Exemplo 4.6.5 - Solução]] f6b32236163de13f9e5bccea6b5ef56a56c6781f 485 484 2015-12-10T03:35:13Z Igorolivei 26 /* Exemplos adicionais relativas a Seção 4.3 */ wikitext text/x-wiki A contagem é fundamental para o estudo da matemática discreta, a complexidade de algoritmos, combinatórios, e alguns ramos da álgebra tais como a teoria do grupo finito. Este capítulo apresenta uma variedade de técnicas que estão disponíveis no Maple para contar uma coleção diversa de objetos discretos, incluindo combinações e permutações de conjuntos finitos. Objetos podem ser contados usando fórmulas ou outros algoritmos, ou listando-os e observando diretamente o tamanho da lista. A última abordagem por um número de procedimentos Maple que pode ser usado para gerar estruturas combinatórias. A maioria dos procedimentos Maple relevantes a este capítulo pertence em um ou dois pacotes. O pacote “combinat” é a parte padrão da versão da biblioteca 3Maple. Um novo pacote “combstruct” está disponível como uma biblioteca compartilhada para MapleV, versão 3, e é um pacote padrão da versão 4. Você pode acessar os serviços oferecidos por qualquer um desses pacotes usando o comando “with” para carregá-lo na sua sessão Maple. (Se você está usando Maple V, versão 3, você também deve colocar with(share) antes de digitar with(combstruct)). É útil saber que o pacote combstruct, enquanto provê uma grande variedade de procedimentos, organiza algumas das funções básicas em grupos relacionados a um objeto combinatório particular (como, por exemplo, combinações ou partições). Para muitos tipos de objetos combinatórios, existem procedimentos Maple para fazer as seguintes operações. # Você pode construir todos os objetos daquele tipo associado a um inteiro dado. Ao procedimento para fazer isso é geralmente dado um nome refletindo o tipo de objeto. (Por exemplo, “permute” and “partitions”.) # Você pode contar todos os objetos daquele tipo associado a um inteiro dado. Aqueles procedimentos geralmente começão com a string “numb” e são completados por uma abreviaçãodo tipo de objeto sendo contado. (Por exemplo, “numbperm” e “numbpart”.) # Você pode gerar um objeto aleatório daquele tipo associado a um inteiro dado. Uma abreviação do tipo de objeto sendo gerado, prefixado com a string “rand” é como essas rotinas são normalmente nomeadas. (Por exemplo, “randperm” e “randpart”.) Claro, também existem muitas outras funções que não se encaixam neste esquema. ==='''1. Funções Maple relevantes'''=== O pacote ''combinat'' contém muitas funções pertinentes à contagem e geração de estruturas combinatórias. A lista de funções neste pacote é: '''''with(combinat);''''' Existe outro pacote, ''combstruct'', disponível no Maple V, versão 4, que também lida com estruturas combinatórias. A maior parte do que este pacote faz está além do escopo deste livro, mas algumas de suas funções expandem o que o pacote ''combinat'' faz. O pacote ''combstruct'' fornece funções '''interstructs'''. '''count''' Para contar o número de objetos de um dado tamanho<br /> '''draw''' Para gerar um objeto aleatório de um dado tamanho<br /> '''allstructs''' Para gerar todos os objetos de um dado tamanho<br /> '''iterstructs''' Para gerar a “próxima” estrutura de um dado tamanho<br /> As estruturas relevantes que ''combstruct'' pode lidar são permutação, combinação/subconjunto, partição. Para acessar os serviços fornecidos pelo pacote ''combstruct'', digite: '''''with(combstruct);''''' Se você estiver usando a versão 3 do Maple, primeiramente você terá que utilizar o comando ''with(share)'', já que o pacote ''combstruct'' é parte da biblioteca na versão 3. As funções no pacote ''combinat'' para combinações são ''numbcomb'', ''choose'', e ''randcomb''. Este é o número de formas de escolher duas frutas a partir de uma maçã, uma laranja e uma pera. '''''numbcomb([apple, orange, pear], 2);''''' Aqui estão as possíveis escolhas: '''''choose([apple, orange, pear], 2);''''' A função ''numbcomb'' conta o número de combinações (ou r-combinações) de um conjunto. A função ''choose'' lista as combinações. Portanto sempre existirão elementos ''numbcomb'' listados por ''choose''. '''''nops(%);''''' E se tivermos duas maçãs e nenhuma pêra (um exemplo com elementos indistinguíveis): '''''numbcomb([apple, apple, orange],2);''''' Com as escolhas: '''''choose([apple, apple, orange],2);''''' Se nós não fornecemos o segundo argumento, todas as combinações possíveis de todos os tamanhos possíveis são consideradas. '''''numbcomb([apple, apple, orange]);<br />''''' '''''choose([apple, apple, orange]);''''' Nós também podemos escolher combinações aleatórias. '''''randcomb([chocolate, vanilla, cookiedough],2);'''''<br /> '''''randcomb(5,3);''''' Neste exemplo, o '''5''' representa o conjunto '''''<math>{1, 2, 3, 4, 5}</math>'''''. Usando ''combstruct'', nós resolveríamos os problemas acima da seguinte forma: '''''count(Combination([apple,orange,pear]),size=2);'''''<br /> '''''allstructs(Combination([apple,orange,pear]), size=2);'''''<br /> '''''draw(Combination([chocolate,vanilla,cookiedough]),size=2);''''' Coeficientes binomiais podem ser calculados tanto chamando a função ''numbcomb'' como um inteiro como primeiro argumento, '''''numbcomb(10,5);''''' ou nós podemos calcular '''''<math>C(n, r)</math>''''', usando a função ''binomial''. Então nós resolvemos o exemplo 7 na seção 4.3 da seguinte forma: '''''binomial(10,5);''''' Quando '''n''' e '''r''' são inteiros não negativos e '''''<math>r \leq n</math>''''', ''binomial'' e ''numbcomb'' se comportam de forma idêntica. O procedimento ''binomial'' é mais geral, e expande a definição dos coeficientes binomiais. Não vamos discutir seu uso mais geral aqui. ==='''2. Mais funções combinatórias'''=== Nesta seção, vamos discutir algumas funções combinatórias, úteis na contagem, que surgem como coeficientes de certos polinomiais. ===='''2.1. Coeficientes binomiais'''==== Os coeficientes binomiais que são coeficientes do polinomial <math>(a+b)^n</math> quando este é expandido. '''''for n from 1 to 7 do'''''<br /> ''''' sort(expand((a + b)^n));'''''<br /> '''''od;'''''' Esses números podem ser acessados diretamente no Maple usando a função ''binomial'' da biblioteca Maple. '''''for n from 1 to 7 do'''''<br /> ''''' seq(binomial(n, k), k = 0..n);'''''<br /> '''''od;''''' O valor do binomial(n, k) é o coeficiente do termo binomial <math>a^kb^{n-k}</math> (que é igual ao coeficiente de <math>a^{n-k}b^k</math>) na expansão de <math>(a+b)^n</math>. Dados argumentos numéricos, ''binomial'' resulta em um número. '''''binomial(100,53);''''' Entretanto, se é dado um argumento simbólico, ''binomial'' retorna indeterminado. '''''n := 'n': # clear values'''''<br /> '''''k := 'k': # from n and k'''''<br /> '''''binomial(n, 9);''''' Você pode expressar isso como uma função racional da variável '''n''' chamando ''expand''. '''''expand(%);''''' Entretanto, isso funciona apenas se no máximo um dos argumentos for simbólico. '''''binomial(n, k);'''''<br /> '''''expand(%);''''' Para determinar a definição, nos termos de fatoriais, você pode usar o comando multifacetado ''convert''. '''''convert(binomial(n, k), factorial);''''' O procedimento ''convert'' é uma utilidade de conversão de propósito geral que pode ser usado para transformar expressões de uma forma para outra, equivalente. Aqui, transforma uma instrução simbólica envolvendo a chamada do procedimento ''binomial'', para uma equivalente expressada usando fatoriais. Devido a ''convert'' aceitar uma grande variedade de tipos de argumentos, sua documentação é espalhada sobre muitas das páginas de ajuda online.Mas um bom lugar para começar a encontrar mais sobre ''convert'', é a página principal de ajuda para este comando, acessada digitando ''?convert''. Essa facilidade pode ser usada para provar identidades combinatórias envolvendo os coeficientes binomiais. Um pouco de cuidado é necessário, entretanto, para levar em conta o grau de avaliação que é realizado a cada passo, deixa coisas que são iguais não serem reconhecidas como tais. Por exemplo, essa identidade famosa <math>\binom{n}{k} = \binom{n}{n-k}</math> pode ser provada da seguinte forma. '''''left := binomial(n, k);'''''<br /> '''''right := binomial(n, n - k);''''' Queremos provar a esquerda e a direita são iguais. Note que '''''evalb(left = right);''''' isso ocorre porque esquerda e direita foram avaliadas de forma insuficiente até o momento. Para superar esta falta de reconhecimento, nós usamos ''convert''. '''''left := convert(left, factorial);'''''<br /> '''''right := convert(right, factorial);'''''<br /> '''''evalb(left = right);''''' Geralmente existe uma certa quantidade de adivinhação envolvida em coagir expressões simbólicas para a forma que é útil para um dado problema. Maple é designado para permitir que você facilmente experimente com expressões, para que você possa descobrir a forma certa para uma aplicação particular. ===='''2.2. Coeficientes multinomiais'''==== Para computar o números de permutações de um conjunto finito em que alguns membros são indistinguíveis do outros (tal conjunto é geralmente chamado um '''multiset'''), Maple fornece o procedimento ''multinomial'' no pacote ''combinat''. Ele calcula os coeficientes multinomiais, isto é, números da forma <math>\frac{n!}{n_1!n_2!n_3!\cdots n_k!}</math> em cada <math>n_1, n_2, n_3, \cdots n_k</math> existem inteiros não negativos cuja soma é '''n'''. O primeiro argumento para ''multinomial'' é o inteiro '''n''', enquanto os argumentos restantes são os números <math>n_1, n_2, \cdots n_k</math> do denominador. Por exemplo, permita-nos computar o número de strings distintas obtidas pela permutação das letras da palavra “MISSISSIPPI” (um exemplo clássico). Aqui existe 1M, e existem 4 Is, 4 Ss, e 2 Ps. Isso dá um total de 11 caracteres. Portanto, o número de strings distintas é '''''combinat[multinomial](11, 1, 4, 4, 2);''''' Observe que o primeiro argumento deve ser a soma dos argumentos restantes; caso contrário um erro é indicado. '''''combinat[multinomial](11, 1, 4, 4, 3);''''' O coeficiente multinomial exibido acima é chamado coeficiente porque ele é o coeficiente do multinomial <math>x_1^{n_1}x_2^{n_2}\cdots x_k^{n_k}</math> na expansão do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Nós podemos ver alguns exemplos disso usando Maple. (Usaremos as variáveis a, b, c, e assim por diante, já que são mais fáceis de se ler que x1, x2, x3, etc.) '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);''''' Existe uma função ''coeff'' que extrai o coeficiente de uma variável num polinomial. '''''coeff(x^3 - 5*x^2 + 2, x^2);'''''<br /> '''''coeff(x^3 - 5*x^2 + 2, x);''''' Entretanto, isso apenas funciona com polinomiais invariáveis. Você pode, todavia, acessar os multinomiais individuais em um polinomial multivariado, usando o comando “op”. '''''op(3, p);'''''<br /> '''''op(p);''''' Isso, infelizmente, depende da ordenação dos multinomiais no polinomial '''p''' fazendo isso impossível de prever qual dentro dos multinomiais em '''p''' será extraída. Para contornar este problema, use o comando ''sort'' primeiro. '''''p := sort(p);'''''<br /> '''''op(3, p);'''''<br /> '''''terms := [op(p)];''''' Os multinomiais são ordenados lexicograficamente . Para reparar a deficiência em ''coeff'' que o impede de manusear polinomiais multivariados, nós podemos escrever nossa própria rotina, ''mcoeff'' que faz esse trabalho para nós. Já que ''coeff'' é implementada no kernel Maple, não é possível para um usuário redefinir seu comportamento, então é necessária uma rotina separada. Para simplicidade, nosso procedimento ''mcoeff'' vai apenas lidar com polinomiais com coeficientes numéricos. O algoritmo usado aqui é o seguinte: #insira um polinomial '''p''' e um termo multinomial ''term''. #processe '''p''' da seguinte: ##ordene '''p''' em '''q''' ##crie uma lista '''r''' de termos multinomiais em '''q'''. ##crie um multiset '''m''' consistido de multinomiais em '''q''' com multiplicidade igual ao coeficiente. (Note que isso não é um multiset verdade, como o coeficiente pode ser negativo ou não integral.) #procure a lista '''m''' para uma entrada combinando '''term''' e, se encontrada, retorne o coeficiente. Caso contrário, retorne 0. Aqui, então, está o código Maple para ''mcoeff''. '''''mcoeff := proc(p::polynom, term::polynom)''''' '''''local m, # list of multinomials''''' '''''t, # index into m''''' '''''x, # dummy variable''''' '''''q, # sorted input''''' '''''r; # multiset of multinomials and coefficients''''' '''''q := sort(p); r := [op(q)];''''' '''''m := map(x -> [coeffs(x), x / coeffs(x)], r);''''' '''''for t in m do''''' '''''if term = op(2, t) then RETURN(op(1, t)); fi;''''' '''''od;''''' '''''RETURN(0);''''' '''''end:''''' Por exemplo, para alocar o coeficiente de no polinomial multivariado , podemos usar ''mcoeff'' da seguinte maneira: '''''p := (a + b + c)^5;'''''<br /> '''''p := expand(p);'''''<br /> '''''mcoeff(p, a^2 * b^3);''''' Solicitar o coeficiente de um multinomial que não esteja no polinomial resulta em zero. '''''mcoeff(p, x^5);''''' Se a entrada polinomial '''p''' é um polinomial em uma única variável, então a chamada ''mcoeff(p, x^n)'' é equivalente à chama ''coeff(p, x^n)'' ou ''coeff(p, x, n)''. (A sintaxe da chamada no último estilo não é suportada por ''mcoeff''.) '''''mcoeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x^2);'''''<br /> '''''coeff(x^3 - 2*x^2 + 1, x, 2);''''' A rotina ''mcoeff'' fornece outros meios em que nós podemos determinar coeficientes multinomiais. Por exemplo: '''''with(combinat):'''''<br /> '''''multinomial(6, 1, 2, 3);'''''<br /> '''''p := expand((a + b + c)^6);'''''<br /> '''''mcoeff(p, a * b^2 * c^3);''''' ===='''2.3. Números Stirling==== Outro conjunto combinatório de números significante que surge como o conjunto de coeficientes de polinomiais especiais é o conjunto de números Stirling. O polinomial Stirling de grau '''n''' é definido por: <math>S_n(x) = x.(x-1).(x-2).\cdots .(x-n+1)</math> Quando expandido, <math>S_n(x)</math> tem a forma: <math>S_n(x) = s(n, 1)x+s(n, 2)x^2+s(n, 3)x^3+\cdots +s(n, n)x^n</math> Os coeficientes <math>S(n, k)</math>, para <math>1\leq k \leq n</math>, são chamados de números Stirling (do primeiro tipo). Podemos usar Maple para gerar os polinomiais Stirling da seguinte forma. '''''n := 'n'; i := 'i';''''' '''''S(n) := product(x - i, i = 0..n-1);''''' Essa expressão Maple insiste em exibir com o uso da função Gamma <math>\Gamma</math>. A função Gamma é uma extensão contínua da função fatorial para números reais. Para um inteiro não negativo '''n''', nós temos <math>\Gamma (n+1) = n!</math>. Mas, para valores específicos de '''n''', podemos coagir Maple a representar os polinomiais de Stirling como polinomiais, usando ''simplify''. '''''subs(n = 9, S(n));'''''<br /> '''''simplify(%);'''''<br /> '''''expand(%);'''''<br /> '''''sort(%);'''''<br /> '''''coeffs(%);'''''<br /> '''''[%];''''' Portanto, nós temos uma lista de números Stirling <math>S(9, k)</math>, para <math>k = 1, 2, \cdots , 9</math>. Você pode acessar os números de Stirling diretamente no Maple, usando a função ''stirling1'' no pacote ''combinat''. '''''with(combinat):''''' '''''for n from 1 to 7 do''''' ''''' seq(stirling1(n,i), i = 1..n);''''' '''''od;''''' Existem alguns padrões interessantes no triângulo resultante. Tente computar mais números de Stirling e veja se você pode fazer quaisquer conjecturas sobre os padrões que você vê. ==='''3. Permutações'''=== Nós já mostramos como contar e gerar combinações usando Maple. Podemos agora introduzir recursos análogos do Maple para trabalhar com permutações. As funções Maple correspondentes para permutações são ''numbperm'', ''permute'' e ''randperm''. Já que todas estão no pacotes ''combinat'', devem ser carregadas antes de serem usadas. '''''with(combinat):''''' '''''numbperm([S,U,C,C,E,S,S]);''''' '''''permute([a,b,c]);''''' '''''randperm([S,U,C,C,E,S,S]);''''' '''''randperm(5);''''' Usando o pacote ''combstruct'', esses exemplos são feitos da seguinte forma: '''''with(combstruct):''''' '''''count(Permutation([S,U,C,C,E,S,S]));''''' '''''allstructs(Permutation([a,b,c]));''''' '''''draw(Permutation(5));''''' A função ''subsets'' permite gerar todos os subconjuntos de um conjunto dado. Já que os subconjuntos e combinações são apenas diferentes nomes para a mesma coisa, você pode usar essa função para gerar combinações. A função ''subsets'' retorna uma tabela que contém duas entradas. Uma é chamada ''nextvalue'', e é um procedimento para gerar a próxima combinação, e a outra é ''finished'', uma flag true/false que informa quando todas elas foram geradas. '''''S := combinat[subsets](a,b):''''' '''''while not S[finished] do''''' ''''' S[nextvalue]();''''' '''''od;''''' Usando ''combstruct'', uma faz a mesma coisa usando a função ''iterstructs''. O procedimento ''iterstructs'' também retorna uma tabela, mas dessa vez usa as funções ''next'' e ''finished'' para iterar. '''''S := iterstructs(Subset(a,b)):''''' '''''while not finished(S) do''''' ''''' nextstruct(S);''''' '''''od;''''' Usando ''iterstructs'', podemos também iterar sobre permutações e tradições. Em adição, nós podemos especificar que tamanho de objeto nós queremos ver. '''''P := iterstructs(Permutation([a,b,b]), size=2):''''' '''''while not finished(P) do''''' ''''' nextstruct(P);''''' '''''od;''''' Pelo fatos das função de permutação Maple poderem resolver problemas de permutação com elementos indistinguíveis tão facilmente quanto sem elementos indistinguíveis, alguns dos exercícios do texto se tornam triviais. Por exemplo, exercício 266 pergunta quantas strings diferentes podem ser formadas com as letras em MISSISSIPPI usando todas as letras. A solução pode ser encontrada em um passo: '''''numbperm([M,I,S,S,I,S,S,I,P,P,I]);''''' A questão 299 é similar, mas envolve alguns passos extras. Ela pergunta quantas strings diferentes podem ser feitas a partir das letras em ORONO, usando uma ou todas as letras. Para achar a solução, primeiramente calculamos o número de 1-permutações, depois com 2-permutações, etc. '''''total := 0:''''' '''''for i from 1 to 5 do''''' ''''' total := total + numbperm([O,R,O,N,O],i);''''' '''''od:''''' '''''total;''''' Existem 633 strings possíveis usando uma ou todas as letras em ORONO. 644 se nós contarmos as string com 0 letras. '''''numbperm([O,R,O,N,O],0);''''' Usando o pacote ''combstruct'', nós podemos achar a resposta em um passo. '''''with(combstruct):''''' '''''count(Permutation([O,R,O,N,O]), size='allsizes');''''' Entretanto, a maior parte dessa sessão envolve pensar e entender a questão. Maple pode ajudar a calcular os números de permutações e combinações, mas cabe a você decidir que valores você precisa calcular para encontrar a resposta. ===='''3.1. Partições de Inteiros'''==== Também existem funções para fazer partições de inteiros. (Uma partição de inteiro é um modo de escrever um inteiro '''n''' como a soma de inteiros positivos, onde ordem não importa. Então <math>5=1+1+3</math> é uma partição de inteiro do 5.) Junto ao ''numbpart'', ''partition'' e ''randpart'', existem funções para gerar partições, uma por vez, baseada em uma dada ordem canônica. Todas estas funções são parte do pacote ''combinat'' que deve, consequentemente, ser carregado antes de você acessá-las. '''''with(combinat):''''' O número de partições de um dado inteiro pode ser contado usando o procedimento ''numbpart''. '''''seq(numbpart(i), i = 1..20);''''' As partições de um inteiro podem ser computadas usando a função ''partition''. '''''partition(5);''''' Isso constrói as partições de seu argumento como uma lista de listas, cada sublista representando uma partição. Como seu nome sugere, ''randpart'' simplesmente cria uma partição aleatória de um inteiro. randpart(20); Maple provê funções especiais para gerar a sequencia de todas as partições de um inteiro dado. Portanto, nós temos as rotinas ''firstpart'', ''nextpart'', ''prevpart'' e ''lastpart''. '''''firstpart(4);''''' '''''nextpart(%);''''' '''''nextpart(%);''''' '''''prevpart(%);''''' '''''nextpart(%%);''''' '''''lastpart(4);''''' ==='''4. Probabilidade discreta'''=== Para encontrar a probabilidade de um evento numa amostra de espaço finita, calcula-se o número de vezes que o evento ocorre, e divide-se pelo número total de resultados possíveis (o tamanho do espaço de amostra). Como no exemplo 4, seção 4.4, nós calculamos a probabilidade de ganhar na loteria, onde precisamos escolher 6 números corretamente de 40 números possíveis. O número total de maneiras de escolher 6 números é: '''''numbcomb(40,6);''''' e existe uma combinação vencedora. Portanto a probabilidade é '''''1/%;''''' a qual nós podemos ver como uma aproximação de um número real usando a função ''evalf'' - avaliação como um número de ponto flutuante. '''''evalf(%);''''' Nós também podemos forçar uma aproximação decimal do resultado usando 1.0, ou simplesmente 1., para mostrar que nós desejamos trabalhar com decimais em vez da representação racional exata. Por exemplo, se precisarmos escolher de 50 números, a probabilidade é: '''''1./numbcomb(50,6);''''' Para outro exemplo do uso do Maple no estudo da probabilidade discreta, permita-nos usar Maple para verificar a asserção no exemplo 144 na página 278 do texto. A afirmação é que o valor esperado do número de sucessos para '''n''' tentativas Bernoulli, cada uma com a probabilidade '''p''' de sucesso, é '''np'''. Nós usaremos '''EV''' para denotar o valor esperado em Maple. (Nós não podemos usar '''E''' porque aquele símbolo é reservado para a base do logaritmo natural.) Nós sabemos que '''''p(X=k) := binomial(n, k) * p^k * (1 - p)^(n - k);''''' A partir da definição, nós temos '''''EV(X) := sum(k * p(X=k), k = 1..n);''''' '''''simplify(%);''''' ==='''5. Gerando combinações e permutações'''=== Aqui está uma implementação do algoritmo para gerar a próxima r-combinação (exemplo 5). '''''NextrCombination := proc(current, n, r)''''' '''''local next, i, j;''''' faça uma cópia que possamos mudar '''''next := table(current);''''' '''''i := r;''''' '''''while next[i] = n - r + i do i := i -1 od;''''' '''''next[i] := next[i] + 1;''''' '''''for j from i+1 to r do''''' '''''next[j] := next[i] + j - i;''''' '''''od;''''' '''''[seq( next[i], i=1..r) ]; # return the answer''''' '''''end:''''' Teste-a no exemplo. '''''NextrCombination([1,2,5,6], 6, 4);''''' '''''NextrCombination(%,6,4);''''' '''''NextrCombination(%,6,4);''''' Alguma explicação é necessária. Primeiro, a combinação atual é uma lista, não um conjunto. Isso é porque a lista é ordenada, mas um conjunto é desordenado. Para encontrar a '''next''' combinação, nós precisamos saber a ordem dos elementos na combinação atual. Mas no Maple, a ordem que digitamos um conjunto e a ordem que aparece dentro do Maple não são necessariamente a mesma coisa. '''''pear, orange, apple;''''' Mas ela sempre a mesma para uma lista. '''''[pear,orange,apple];''''' O próximo problema é que você não pode, antes da versão 4 do Maple V, atribuir um elemento específico dentro de uma lista. '''''mylist := [a,b,c,d]:''''' '''''mylist[2] := e;''''' Então, a primeira coisa que fazemos nesse algoritmo é fazer uma tabela que contém todos os elementos na combinação. Nós podemos atribuir na tabela, então nosso problema acaba. '''''mytable := table(mylist);''''' '''''mytable[2] := e;''''' '''''print(mytable);''''' Com o pacote ''combstruct'', você pode criar um iterador que vai produzir todos os objetos de um certo tamanho, um por vez. '''''it := iterstructs(Combination(6),size=4):''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' '''''nextstruct(it);''''' Chamando essa função algumas vezes mais, nos leva a: '''''nextstruct(it);''''' onde a próxima 4-combinação é então: '''''nextstruct(it);''''' pela qual nós podemos ver que esse iterador está usando a mesma lexicografia ordenando como usamos no algoritmo 3. ==='''6. Computações e explorações'''=== =====1. Dado um inteiro positivo ''n'', encontre a probabilidade de selecionar seis inteiros do conjunto {<math>1, \cdots , n</math>} que foram mecanicamente selecionados em uma loteria. ===== '''Solução''' Nós seguiremos o exemplo 4 no texto. O número total de maneiras de escolher 6 números de '''n''' números é <math>C(n, 6)</math>, que pode ser encontrado com o procedimento ''numbcomb'' no pacote ''combinat''. Isso nos dá o número total de possibilidades, onde apenas uma irá vencer. '''''Lottery := proc(n::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n, 6); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery(49); ''''' Se as regras da loteria mudarem, para que o número de números escolhidos seja algo diferente de 6, então nós devemos modificar o procedimento acima. (Por exemplo, talvez agora possamos escolher 5 números de 499, em vez de 6.) Nós podemos facilmente modificar nosso programa para nos deixar especificar quantos números nós queremos escolher adicionando outro parâmetro. '''''Lottery2 := proc(n::posint, k::posint) ''''' '''''local total; ''''' ''''' total := combinat[numbcomb](n,k); ''''' ''''' 1.0 / total; ''''' '''''end: ''''' '''''Lottery2(49,6); ''''' '''''Lottery(30,3); ''''' =====2. Dados inteiros positivos ''n'' e ''r'', liste todas as r-combinações, com repetições permitidas, do conjunto .===== '''Solução''' A função ''choose'' do Maple (no pacote ''combinat''), vai listar todas as r-combinações de, mas sem repetições. Portanto nós não podemos usá-la diretamente. Entretanto, digamos que queremos todas as 2-combinações de, com repetições. Isso quer dizer que junto com , e , nós também queremos incluir, e . Nós queremos ser capazes de escolher cada número até 2 vezes. (Nós dizemos que podemos repetir um elemento qualquer número de vezes, mas na prática, já que nós apenas podemos escolher 2 coisas no total, nós só precisamos permitir cada número aparecer no máximo 2 vezes.) Então outra forma de olhar o problema é dizer que queremos todas as 2-combinações, sem repetição, do conjunto. Em geral, então, nós podemos encontrar todas as r-combinações de com repetição pedindo por todas as r-combinações, onde cada elemento aparece '''r''' vezes. '''''RCombRepetition := proc(n::posint, r::posint) ''''' '''''local repeatlist, i; ''''' ''''' repeatlist := [ seq( i $ r, i=1..n) ]; ''''' ''''' combinat[choose](repeatlist, r); ''''' '''''end: ''''' '''''RCombRepetition(3,2); ''''' '''''RCombRepetition(4,3); ''''' (Notas sobre o procedimento: O '''i $ r''' significa repetir '''i r''' vezes. '''''1 $ 3; ''''' '''''happy $ 4; ''''' Além disso, nós precisamos usar uma lista em vezes de um conjunto, já que o Maple automaticamente remove elementos repetidos em um conjunto e nós perderíamos todas as repetições.) '''''happylist := [ happy $ 4]; ''''' '''''happyset := happy $ 4 ; ''''' =====3. Encontre o número de resultados possíveis em uma partida de dois times quando o vencedor é o primeiro time a ganhar 5 de 9, 6 de 11, 7 de 13 ou 8 de 15 jogos.===== '''Solução''' Nossa solução vai usar o procedimento Maple chamado ''permute'' para computar o número total de maneiras que um torneio de jogos pode ser jogado. Vamos começar construindo duas listas que observa como cada um dos dois times pode ganhar. Nós iremos atribuir as duas do time 1 vencendo o torneio sem nenhuma derrota, e o time 2 vencendo o torneio sem nenhuma derrota. A cada iteração do loop principal do algoritmo, vamos computar as permutações possíveis de jogos a serem jogados, notando que a ordem de vitórias é importante para nós. Após essas permutações serem calculadas, nós vamos aumentar o número de jogos que o torneio dura (ou seja, permite o eventual time perdedor do torneio a vencer um jogo adicional). Isso é equivalente a usar um diagrama de árvore para computar os resultados possíveis. O loop externo (''while'') corresponde ao nível de vértices na árvore, e o loop interior (for) itera sobre todos os jogos naquele nível. A implementação Maple dessa descrição é mostrada abaixo. '''''Tournaments:=proc(games::integer) ''''' ''''' local i, one_wins, two_wins, Temp, S; ''''' Inicialize uma lista para garantir que o time 1 vença ''''' one_wins:=[seq(1, i=1..ceil(games/2))]; ''''' Inicialize uma lista para garantir que o time 2 vença ''''' two_wins:=[seq(2, i=1..ceil(games/2))]; ''''' ''''' S:={}; ''''' Percorra até nós termos todos os jogos da série usados ''''' while nops(one_wins) <= games do ''''' Calcule os resultados possíveis que completam em jogos exatos ''''' Temp:=permute(one_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 1) ''''' if Temp[i][nops(one_wins)] = 1 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Calcule os resultados possíveis que completa em jogos exatos ''''' Temp:=permute(two_wins); ''''' ''''' for i from 1 to nops(Temp) do ''''' Garanta que nós realmente precisamos de todos os jogos (ou seja, o último jogo da série foi vencido pelo time 2) ''''' if Temp[i][nops(two_wins)] = 2 then ''''' ''''' S:=S union Temp[i] ''''' ''''' fi; ''''' ''''' od; ''''' Incremente o número de jogos, para que o time vencedor do torneio perca um jogo a mais. ''''' one_wins:=[op(one_wins), 2]; ''''' ''''' two_wins:=[op(two_wins), 1]; ''''' ''''' od; ''''' ''''' S; ''''' '''''end: ''''' Agora nós usamos esse procedimento recentemente criado em torneios que são o melhor de “3-de-5” e o melhor de “4-de-7” em número de jogos. '''''Tournaments(5); ''''' '''''nops(%); ''''' '''''nops(Tournaments(7)); ''''' Ao leitor é deixado explorar os casos restantes, e conjecturar uma fórmula no caso geral. =====4. Nós queremos olhar para os coeficientes binomiais <math>C(2n, n)</math>. Especificamente, para muitos exemplos, nós queremos determinar se <math>C(2n, n)</math> é divisível pelo quadrado de um primo, e se o maior expoente na fatorização do primo cresce sem limites enquanto ''n'' cresce.===== '''Solução''' Primeiro tentaremos um exemplo, para ver o que exatamente desejamos fazer, e então escrever um programa. '''''c := binomial(6,3); ''''' Nós usamos a função ''ifactors'' (o '''i''' significa '''integer''') para fatorar '''c'''. Essa função é uma das várias do Maple que deve ser definida '''readlib''' antes que possamos usá-la. Isso significa que pedimos para o Maple encontrar a função na sua biblioteca, e carregá-la na sessão atual. '''''readlib(ifactors): ''''' '''''ifacts := ifactors(c); ''''' A página de ajuda para ''ifactors'' explica o que este resultado significa. Ela diz que <math>20 = 1.2^2.5^1</math>. Nós estamos interessados nos expoentes dos primos. Primeiro, pegamos o segundo elemento da lista, para obter a lista dos primos e expoentes. '''''facts := ifacts[2]; ''''' Isso nos dá uma lista de listas, onde o primeiro elemento em cada lista é o fator primo, e o segundo é a multiplicidade (o número de vezes que o fator aparece) daquele primo. Então nós queremos percorrer a lista e obter o segundo elemento de cada sublista. '''''powers := seq(x[2],x=facts); ''''' Então nós usamos a função ''max'' para encontrar o maior expoente. '''''max(powers); ''''' Se o maior exemplo é maior que 1, então <math>C(2n, n)</math> é divisível pelo quadrado de um primo. Nesse caso, o maior exemplo 2 é, de fato, maior que 1, e <math>C(6, 3)</math> sem dúvida é divisível por <math>5^2</math>. Combinando esses passos, agora nós escrevemos um programa que dado '''n''', retorna o maior expoente na fatorização de <math>C(2n, n)</math>. '''''LargestExpon := proc(n) ''''' '''''local c, ifacts, x; ''''' ''''' c := binomial(2*n,n); ''''' ''''' ifacts := ifactors(c); ''''' ''''' max(seq(x[2],x=ifacts[2])); ''''' '''''end: ''''' '''''LargestExpon(6); ''''' Agora nós vamos escrever outra rotina que vai calcular o maior expoente para muitos valores de '''n''', e armazenar os resultados numa tabela. '''''Manyn := proc(maxn) ''''' '''''local results, i; ''''' ''''' for i to maxn do ''''' ''''' results[i] := LargestExpon(i); ''''' ''''' if results[i] = 1 then ''''' ''''' printf(`Hurray! A counterexample! %d`, i); ''''' ''''' fi; ''''' ''''' od; ''''' ''''' eval(results); ''''' '''''end: ''''' Rode o programa e veja o que acontece. '''''Manyn(10): ''''' Parece que 1, 2 e 4 são valores de '''n''' tais que <math>C(2n, n)</math> não é divisível pelo quadrado de um primo. '''''binomial(8,4); ''''' '''''ifactors(%); ''''' Agora deixe o programa rodar por muito mais tempo, e veja se nós podemos encontrar algo mais. '''''vals := Manyn(200): ''''' Vamos olhar para o crescimento do expoente máximo representando graficamente os resultados. '''''plot([ seq([i,vals[i]],i=1..200)],style=POINT, ''''' '''''title=`Growth of Largest Exponents`); ''''' Para comparar, tente novamente com ainda mais valores de '''n'''. '''''vals := Manyn(300): ''''' Dessa vez, plote com os pontos que participaram, para ver que diferença isso faz. '''''plot([ seq([i,vals[i]],i=1..300)], ''''' '''''title=`Growth of Largest Exponents 2`); ''''' É difícil encontrar quaisquer conclusões desses dois gráficos, além de que não parece ser um limite para o tamanho. O tempo de cálculo está se tornando longo, mas ainda podemos olhada para alguns exemplos maiores. '''''LargestExpon(500); ''''' '''''LargestExpon(1001); ''''' '''''LargestExpon(1005); ''''' '''''LargestExpon(1007); ''''' '''''LargestExpon(1009); ''''' =====5 . Estime a probabilidade que dois inteiros escolhidos aleatoriamente sejam relativamente primos testando um grande números de pares de inteiros aleatoriamente selecionados. Observe o teorema que dá essa probabilidade e compare seus resultados com a probabilidade correta.===== '''Solução''' Para resolver esse problema, três coisas devem ser feitas. #Crie um método para gerar pares de inteiros aleatórios. #Produza um grande número desses pares, testando se eles são relativamente primos, e observe a probabilidade estimada baseada nessa amostra. #Observe o teorema mencionado em questão. Naturalmente, nós deixaremos a parte 3 inteiramente para o leitor. Uma simples aproximação é usar o procedimento do Maple ''rand'' para gerar uma lista de inteiros aleatórios. Então, tendo gerado tal lista nós podemos testar a coprimalidade de seus membros em pares usando o procedimento Maple ''igcd'' em um segundo loop. Nós implementamos esses dois loops em um novo procedimento Maple chamado ''RandPairs'': '''''RandPairs := proc(list_size::integer) ''''' ''''' local i, tmp, randnums, count; ''''' ''''' randnums := NULL; ''''' Gera a lista de inteiros aleatórios ''''' for i from 1 to list_size do ''''' ''''' tmp := rand(); ''''' ''''' randnums := randnums, tmp(); ''''' ''''' od; ''''' ''''' randnums := [randnums]; ''''' Conta o números de pares que são coprimos ''''' count := 0; ''''' ''''' for i from 1 by 2 to list_size-1 do ''''' ''''' if igcd(randnums[i], randnums[i + 1]) = 1 then ''''' ''''' count := count + 1; ''''' ''''' fi; ''''' ''''' od; ''''' ''''' count; ''''' '''''end: ''''' Podemos agora executar esse procedimento em 1000 pares de inteiros, como a seguir: '''''RandPairs(200); ''''' Então, podemos determinar a porcentagem de pares coprimos usando esse resultado. '''''evalf(RandPairs(200)/100); ''''' Observe que repetindo a computação idêntica pode muito bem levar a um resultado de certa forma diferente já que a lista de inteiros que usamos foi gerada aleatoriamente. Você deve tentar isso como uma amostra de tamanho muito maior, digamos 10000 pares de inteiros. =====6. Determine o número de pessoas necessárias para assegurar que a probabilidade de apenas duas delas terem o mesmo dia do ano como seu aniversário é pelo menos 700 porcento, pelo menos 800 porcento, pelo menos 900 porcento, pelo menos 955 porcento, pelo menos 988 porcento, e pelo menos 999 por cento.===== '''Solução''' Dado que sabemos a fórmula para a probabilidade de duas pessoas fazerem aniversário no mesmo dia, nós podemos usar Maple para percorrer uma variedade de número de pessoas possíveis, até que alcancemos a probabilidade maior que a probabilidade desejada. Se considerarmos a probabilidade que nenhuma dupla de pessoas possuem o mesmo aniversário como '''p''', nós podemos determinar a probabilidade de que apenas duas pessoas nasceram no mesmo dia do ano como <math>1-p</math>. Para determinar o que é '''p''', observamos que se nós temos k pessoas, a primeira pessoa possui a probabilidade de 1 que ter o mesmo aniversário que ela mesma. A segunda pessoa tem 364 outros dias de 365 para escolher para que ela não faça aniversário no mesmo dia que a primeira pessoa. Similarmente para a pessoa <math>3, 4, \cdots , k</math>, onde a k-gésima pessoa tem <math>365-k</math> escolhas. Tomando o produto dessas probabilidades, concluímos que <math>p=P(365,k)/365^k</math>, que nos permite facilmente computar <math>1-p</math>. Agora nós representamos e combinamos essa informação num procedimento Maple chamado “Birthdays”. '''''Birthdays := proc(percentage::float) ''''' ''''' local num_people, cur_prob; ''''' Inicializa ''''' cur_prob := 0; num_people:=0; ''''' Percorre enquanto houver pessoas ''''' while cur_prob < percentage do ''''' ''''' num_people := num_people + 1; ''''' ''''' cur_prob := 1 ''''' ''''' -(numbperm(365,num_people) / 365^num_people); ''''' ''''' od; ''''' ''''' RETURN(num_people); ''''' '''''end: ''''' Esse procedimento retorna o número de pessoas requeridas para atingir a probabilidade dada de que duas pessoas tenho o mesmo aniversário. Agora nós executamos nosso procedimento em alguns casos de teste, para probabilidades de 0.70, 0.80 e 0.90; '''''Birthdays(.70); ''''' '''''Birthdays(.80); ''''' '''''Birthdays(.90); ''''' ==='''7. Exercícios/Projetos'''=== 1. Use Maple para gerar várias filas do triângulo de Pascal, veja se você pode formular algumas conjecturas satisfeitas pelos coeficientes binomiais C(n,k). 2. Use o Maple para determinar quantas palavras diferentes podem ser feitas com a palavra PAPARRAZZI quando todas as letras forem usadas; quando algum número de letras forem usadas; quando todas as letras forem usadas e a palavra começa e termina com a letra Z; quando todas as letras são usadas e os três A’s são consecutivos. 3. Use o Princípio da casa dos pombos para projetar e então implementar um procedimento Maple que encontre a subsequência crescente máxima de uma dada sequência de números. (Veja a página 2455, et seq no seu texto.) 4. Suponha que um certo Departamento de Matemática possui “m” professores e “f” professoras. Escreva um procedimento maple para encontrar todos os comitês com 2000 membros em que ambos os sexos são representados igualmente. 5. Use Maple para provar a identidade <math>\binom{n+1}{k} = (n+1)\binom{n}{k-1}/k</math>, para inteiros positivos n e k com <math>k \leq n</math>. 6. Use Maple para provar a identidade de Pascal: <math>C(n+1, k) = C(n, k-1)+C(n, k)</math>, para todos os inteiros positivos n e k com <math>k \geq n</math>. 7. Use Maple para determinar o inteiro k ao qual as chances de se pegar seis números corretamente em uma loteria dos primeiros k inteiros positivo é menor que #1 em 1000 milhões, #1 em um bilhão (10^9), #1 em 100 bilhões, #1 em 1000 bilhões, e #1 em um trilhão (10¹²). 8. Use Maple para contar e listar todas as soluções para a equação <math>x_1+x_2+x_3+x_4 =25</math> onde <math>x_1</math>, <math>x_2</math>, <math>x_31</math> e <math>x_4</math> são inteiros não negativos. 9. Gere um grande triângulo de números Stirling e procure por padrões que sugerem identidades entre os números Stirling. (Um pequeno triângulo foi mostrado na seção 4.22.) Você pode fazer quaisquer conjecturas sobre a relação entre os números de Stirling e os coeficientes binomiais? 10. Escreva uma função Maple que recebe como entrada três inteiros positivos n, k e i, e returna o i-ésimo multinomial, em ordem lexicográfica, do polinomial <math>(x_1+x_2+\cdots +x_k)^n</math>. Escreva seu inverso; isto é, dado um multinomial, o inverso deve retornar seu índice (posição) no polinomial ordenado. 11. Escreva um programa Maple para computar a expansão de Cantor de um inteiro. (Veja página 2988 do livro.) 12. Implemente, em Maple, o algoritmo para gerar o conjunto de todas as permutações dos primeiros “n” inteiros, usando a bijeção da coleção de todas as permutações do conjunto {<math>1, 2, \cdots , n</math>} para o conjunto {<math>1, 2, \cdots , n!</math>} descrito anteriormente no exercício 100 na página 2988 do livro. 13. Escreva um procedimento Maple para gerar permutações aleatórias como descritas no exercício 144 da página 2988 do livro. =='''Exemplos Extras'''== ===Exemplos extras da seção 4.1=== ====Exemplo 4.1.1==== ''Há 3 voos disponiveis de Indianapolis para St.Louis e, independentemente de quais desses voos será escolhidos, há 5 voos disponiveis de St.Louis para Dallas.De quantas maneiras uma pessoa pode voar de Indianapolis para St.Louis para Dallas? (pág 302)'' [[Exemplo 4.1.1 - Solução]] ====Exemplo 4.1.2==== ''Um certo tipo de botao de uma fechadura de porta exige que voce insira um codigo antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.'' ''(a) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, com números repetidos permitidos, quantos códigos de entrada são possíveis?'' ''(b) Se voce escolher um código de entrada que consiste de uma sequencia de 4 digitos, sem repetir os números, quantos códigos de entrada são possíveis?'' [[Exemplo 4.1.2 - Solução]] ====Exemplo 4.1.3==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.3 - Solução]] ====Exemplo 4.1.4==== ''Conte os numeros de instruções de impressão nesse algoritmo: <nowiki> de i=1 até n inicio de j=1 ate n print hello de k=i+1 ate n print hello fim </nowiki>'' [[Exemplo 4.1.4 - Solução]] ====Exemplo 4.1.5==== ''Encontre o numero de palavras com 10 letras sem repeti-las:'' ''(a) que não tenha vogais.'' ''(b) que começam com uma vogal.'' ''(c) que tenha C e V nas extremidades (em qualquer ordem). ''(d) que tenha vogais nas duas primeiras posições. [[Exemplo 4.1.5 - Solução]] ====Exemplo 4.1.6==== ''10 homens e 10 mulheres estão em uma fila: ''(a) Encontre quantas possibilidades pode ser formada a fila. ''(b) Encontre quantas possibilidades pode ser formada a fila se duas pessoas do mesmo sexo não podem ficar lado a lado; ''(c) Encontre quantas possibilidades pode ser formada a fila se Beryl, Carol, e Darryl querem ficar juntas nesta sequencia (Carol, Beryl, and Darryl; ou Darryl, Beryl, e Carol). [[Exemplo 4.1.6 - Solução]] ====Exemplo 4.1.7==== ''Encontre o número de palavras 10 letras : ''(a) Não contenha vogais. ''(b) Começar com uma vogal. ''(c) Ter vogais nas duas primeiras posições. ''(d) Começar com C e terminam com V. ''(e) Começar com C ou terminar com V. ''Para resolver o problema é ter em mente uma fila de dez espaços em branco : [[Exemplo 4.1.7 - Solução]] ===Exemplos da Seção 4.2=== ===== Exemplo 4.2.1 ===== ''Provar que em qualquer grupo de três números inteiros positivos, existem pelo menos dois, cuja a soma é par. (Pág. 314)'' [[Exemplo 4.2.1 - Solução]] ===== Exemplo 4.2.2 ===== ''Se forem escolhidos inteiros positivos aleatoriamente, qual é o número mínimo que podemos garantir que dois dos números escolhidos sejam congruentes módulo 6. (pág 314)'' [[Exemplo 4.2.2 - Solução]] ===== Exemplo 4.2.3 ===== ''Prove que em qualquer conjunto de 700 palavras em inglês, deve haver pelo menos duas que começam com o mesmo par de letras (na mesma ordem), por exemplo, ST OP e STAndard.(pág 314)'' [[Exemplo 4.2.3 - Solução]] ===== Exemplo 4.2.4 ===== ''Cada tipo de peça de uma máquina feita em uma fábrica é carimbada com um código do formulário de letter-digit-digit, onde os dígitos podem ser repetidos. Prove que, se 8000 peças são feitas, então, pelo menos, quatro delas devem ter o mesmo código carimbadas.(pág. 315)'' [[Exemplo 4.2.4 - Solução]] ===== Exemplo 4.2.5 ===== ''Cada aluno é classificado como um membro de uma das seguintes classes: Freshman, Sophomore, Junior, Senior. Encontrar o número mínimo de estudantes que devem ser escolhidos de modo a garantir que, pelo menos, oito pertencem à mesma classe.(pág. 315)'' [[Exemplo 4.2.5 - Solução]] ===Exemplos adicionais relativos a Seção 4.3=== '''Exemplo 4.3.1''' ''Uma classe tem 30 alunos matriculados. De quantas maneiras pode-se: (pág 321) ''(a) Colocar 4 alunos em uma fila para uma foto? ''(b) Colocar todos os 30 alunos em uma fila para uma foto? ''(c) Colocar todos os 30 alunos em duas filas de 15 cada para uma foto?'' [[Exemplo 4.3.1 - Solução]] '''Exemplo 4.3.2 ''' ''Um certo tipo de botão de uma fechadura de porta exige que você insira um código antes que a fechadura abra.O bloqueio tem 5 botoes, numerados de 1 a 5.O bloqueio é programado para reconhecer seis códigos de 4 dígitos diferentes, podendo repetir os algarismos de cada código. Quantos conjuntos diferentes de códigos reconhecíveis existem?(pág 324)'' [[Exemplo 4.3.2 - Solução]] '''Exemplo 4.3.3 (pág 324)''' ... [[Exemplo 4.3.3 - Solução]] '''Exemplo 4.3.4''' ''Quantas maneiras existem de escolher uma comissão de cinco pessoas consistindo de três mulheres e dois homens de um grupo de dez mulheres e sete homens?(pág 324)'' [[Exemplo 4.3.4 - Solução]] '''Exemplo 4.3.5 ''' ''Sendo o conjunto S = {1,2,3,...,19}. Encontre o número de subconjuntos de S com numeros iguais de inteiros pares e impares.(pág 324)'' [[Exemplo 4.3.5 - Solução]] '''Exemplo 4.3.6 ''' ''Encontre maneiras de dividir um baralho de 52 cartas, em:(pág 324)'' ''a)Em 4 pilhas iguais, classificado em A,B,C,D; ''b)Em 4 pilhas iguais, sem classificação;'' [[Exemplo 4.3.6 - Solução]] '''Exemplo 4.3.7 ''' ''Suponha que S = {1,2, . . ., 25} . Encontre o numero de subconjuntos de tamanho 5,tal que T:(pág 324)'' ''a) consista de 2 numeros impares e 3 numeros pares. ''b) consiste de exatamente três números primos. ''c) tenha a soma dos seus elementos, menor que 20. ''d) tem, pelo menos, um número par na mesma.'' [[Exemplo 4.3.7 - Solução]] ===Exemplos adicionais relativas a Seção 4.4=== '''Exemplo 4.4.1 ''' ''Escreva a expansão de (x+2y)³. (pág 328)'' [[Exemplo 4.4.1 - Solução]] '''Exemplo 4.4.2 ''' ''Encontre o coeficiente <math>a^{17}b^{23}</math> na expansão de <math>(3a-7b)^{40}</math>. (pág 328)'' [[Exemplo 4.4.2 - Solução]] '''Exemplo 4.4.3 ''' ''Escreva a expansão de <math>(x^2-\frac{1}{x} )^8</math>. (pág 328)'' [[Exemplo 4.4.3 - Solução]] ===Exemplos adicionais relativas a Seção 4.5=== '''Exemplo 4.5.1 ''' ''Uma padaria vende quatro tipos de biscoitos: chocolate, geleia, açúcar, manteiga de amendoim. Você pode comprar um saco com 30 biscoitos. Assumindo que a padaria tem pelo menos 30 de cada tipo de biscoito, quantos sacos contendo 30 biscoitos você poderia comprar se você deve escolher: (pág 338)'' ''a) Ao menos 3 biscoitos de chocolate e pelo menos 6 biscoitos de manteiga de amendoim ''b) Exatamente 3 biscoitos de chocolate e exatamente 6 biscoitos de manteiga de amendoim ''c) No máximo 5 biscoitos de açúcar ''d) Pelo menos um dos quatro tipos de biscoitos.'' [[Exemplo 4.5.1 - Solução]] '''Exemplo 4.5.2 ''' ''Quantos anagramas podem ser formados pela palavra DECEIVED? (pág 339)'' [[Exemplo 4.5.2 - Solução]] '''Exemplo 4.5.3''' ''Um frasco contém 30 moedas de 1 centavo, 20 moedas de 5 centavos, 20 moedas de 10 centavos, e 15 moedas de 25 centavos. (As moedas de cada denominação são consideradas idênticas.) (pág 339)'' ''(a) Encontre o número de maneiras de colocar todas as 85 moedas em uma fileira. ''(b) Encontre o número de possíveis ‘punhados’ de 12 moedas.'' [[Exemplo 4.5.3 - Solução]] '''Exemplo 4.5.4''' ''De quantas maneiras é possivel colocar 7 das 8 letras de “CHEMISTS” em uma fila? (pág 339)'' [[Exemplo 4.5.4 - Solução]] ===Exemplos adicionais relativas a Seção 4.6=== '''Exemplo 4.6.1''' ''Coloque as seguintes permutações de 1, 2, 3, 4, 5, 6, na ordem lexicográfica : (pág 345)'' <math>461325, 326145, 516243, 324165, 461235, 324615, 462135</math> [[Exemplo 4.6.1 - Solução]] '''Exemplo 4.6.2''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente após 263.541 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6.2 - Solução]] '''Exemplo 4.6.3 ''' ''Encontre a permutação de 1, 2, 3, 4, 5, 6 imediatamente antes de 261.345 em ordem lexicográfica. (pág 345)'' [[Exemplo 4.6.3 - Solução]] '''Exemplo 4.6.4 ''' ''Se as permutações de 1,2,3,4,5,6 forem colocadas em ordem lexicográfica, com 123.456 na posição 1, 123.465 na posição 2, etc., encontrar a permutação na posição 362. (pág 345)'' [[Exemplo 4.6.4 - Solução]] '''Exemplo 4.6.5 ''' ''Se as permutações de 1,2,3,4,5 forem colocadas em ordem lexicográfica, em que posição estará a permutação 41253? (pág 345)'' [[Exemplo 4.6.5 - Solução]] 4809359911f3e2c7413ad2e40b2f55715dc8a112 Exemplo 4.4.1 - Solução 0 112 466 2015-12-10T03:21:10Z Igorolivei 26 Created page with "'''Solução:''' Pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</..." wikitext text/x-wiki '''Solução:''' Pelo teorema binomial: <math>(x+2y)^3 = \binom{3}{0} x^3(2y)^0+\binom{3}{1} x^2(2y)^1+\binom{3}{2} x^1(2y)^2+\binom{3}{3} x^0(2y)^3 = x^3+6x^2y+12xy^2+8y^3</math> af95cee7bfcf84e1731e08e0781216d738f4ebfe Exemplo 4.4.2 - Solução 0 113 467 2015-12-10T03:21:27Z Igorolivei 26 Created page with "'''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficien..." wikitext text/x-wiki '''Solução:''' Expandindo <math>(3a-7b)^{40}</math> usando o teorema binomial, localizamos o termo com o produto <math>a^{17}b^{23}</math>, e então encontramos o coeficiente: <math>(3a-7b)^{40} = (3a+(-7b))^{40}</math> = <math>\cdots + \binom{40}{17} (3a)^{17}(-7b)^{23} + \cdots</math> = <math>\cdots + \binom{40}{17} 3^{17}(-7)^23a^{17}b^{23} + \cdots</math> Assim, o coeficiente de <math>a^{17}b^{23}</math> é <math>\binom{40}{17} 3^{17}(-7)^{23}</math>, que também pode ser escrito como <math>\binom{40}{23} 3^{17}(-7)^{23}</math>. 4950b457fe12c7c973568533b294482e410ae6ba Exemplo 4.4.3 - Solução 0 114 468 2015-12-10T03:21:42Z Igorolivei 26 Created page with "'''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2..." wikitext text/x-wiki '''Solução:''' Usa-se o teorema binomial. Em seguida, várias regras exponenciais para simplificar os termos. <math>(x^2-\frac{1}{x} )^8 = \sum_{i=0}^{8} \binom{8}{i} (x^2)^i(\frac{-1}{x} )^{8-i}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} \frac{x^{2i}(-1)^{8-i}}{x^{8-i}}</math> <math>= \sum_{i=0}^{8} \binom{8}{i} x^{3i-8}(-1)^{8-i}</math> <math>= x^{-8}-8x^{-5}+28x^{-2}-56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> <math>= \frac{1}{x^8} -\frac{8}{x^5} +\frac{28}{x^2} -56x^{1}+70x^{4}-56x^{7}+28x^{10}-8x^{13}+x^{16}</math> c32dfc35ce30cfa2ad52ece8685d4349842e2399 Exemplo 4.5.1 - Solução 0 115 471 2015-12-10T03:26:46Z Igorolivei 26 Created page with "'''Solução:'''" wikitext text/x-wiki '''Solução:''' cc7042bdaf49d68969959d29537e8d4d48049d9d Exemplo 4.5.2 - Solução 0 116 472 2015-12-10T03:27:00Z Igorolivei 26 Created page with "'''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!..." wikitext text/x-wiki '''Solução: ''' Na palavra há dois ‘D’, três ‘E’, um ‘C’, um ‘I’ e um ‘V’. Portanto, o número de permutações de DECEIVED é: <math>\frac{8!}{2!.3!.1!.1!.1!} = \frac{8!}{2!.3!}</math> 6d3489916b8d268c91eb6231d91bbc187b7170ca Exemplo 4.5.3 - Solução 0 117 473 2015-12-10T03:27:16Z Igorolivei 26 Created page with "'''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q..." wikitext text/x-wiki '''Solução:''' (a) A resposta não é 85! uma vez que as moedas não são todos distintos. Pense no problema como um de fazer uma palavra com 30 p's, 20 n's, 20 d's, e 15 q's. Tendo em conta as cartas idênticas, temos <math>\frac{85!}{30!.20!.20!.15!}</math> (b) Quando se contar o número de ‘punhados’ de 12 moedas, estamos apenas preocupados com o número de cada denominação escolhida. Por exemplo, poderíamos escolher 9 moedas de 1 centavos, 2 de 5 centavos, e uma de 25 centavos, ou podemos escolher três de cada denominação. Assim, o número de um ‘punhados’ de 12 moedas é igual ao número inteiro não negativo de soluções para a equação: <math>p+n+d+q = 12</math> onde P é o número de moedas de 1 centavo, n é o número de moedas de 5 centavos, d é o número de moedas de 10 centavos, e q é o número de 25 centavos. O número de soluções para esta equação é: <math>C(15,3) = 455</math> f4fd7bd854397a6ebcfc239cb01340f9ef803ff7 Exemplo 4.5.4 - Solução 0 118 474 2015-12-10T03:27:30Z Igorolivei 26 Created page with "'''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem seleciona..." wikitext text/x-wiki '''Solução:''' Existem dois padrões a serem considerados: (a) 7 letras distintas são selecionados (ou seja, apenas um S é selecionado), e (b) os dois S serem selecionados. No primeiro teste padrão, existem 7! Maneiras de colocar as 7 letras distintas em uma fileira. No segundo padrão, as sete letras selecionadas têm dois S’s, por isso há 7! / 2! Maneiras de colocar essas letras em uma fileira. Adicionando os totais obtidos a partir dos dois casos, temos o número total de maneiras de colocar sete dos oito cartas em uma fileira: <math>7!+6.\frac{7!}{2!}</math> e0bb1ec68b3b28b5e85184a33fc4185684e5f080 Exemplo 4.6.1 - Solução 0 119 479 2015-12-10T03:33:44Z Igorolivei 26 Created page with "'''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243" wikitext text/x-wiki '''Solução:''' Procedendo do menor ao maior, as permutações são: 324165, 324615, 326145, 461235, 461325, 462135, 516243 72042bc9801bcaca8ccc528394ee3d9e499c2461 Exemplo 4.6.2 - Solução 0 120 480 2015-12-10T03:33:57Z Igorolivei 26 Created page with "'''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos re..." wikitext text/x-wiki '''Solução:''' Os dígitos 5, 4, 1 estão em ordem decrescente, por isso precisamos aumentar o dígito seguinte, 3. Substitui-lo por 4 e, em seguida, colocar os dígitos restantes em ordem crescente, temos 264.1355. 4bd89f3394600d1763e42d1a9aaddc0dfda092f9 Exemplo 4.6.3 - Solução 0 121 481 2015-12-10T03:34:10Z Igorolivei 26 Created page with "'''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda pos..." wikitext text/x-wiki '''Solução:''' Uma vez que os quatro últimos dígitos, 1345, estão em ordem crescente, a permutação que vem imediatamente antes deste deve ter um “5” na segunda posição e os quatro dígitos após o “5”, em ordem decrescente. Assim, o antecessor de 261.345 é 256.431. 734d378c033d3b727b0acd717970f50e8c20cb55 Exemplo 4.6.4 - Solução 0 122 482 2015-12-10T03:34:21Z Igorolivei 26 Created page with "'''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120..." wikitext text/x-wiki '''Solução:''' Existem 6! = 720 permutações de 1, 2, 3, 4, 5, 6. O primeiro 120 (isto é, as permutações em posições de 1 a 120) começa com um “1”, o segundo 120 (nas posições 121 a 240) começar com “2”, etc. Assim, a primeira permutação começando com “4”, 412,356, é na posição 361. Assim , a próxima permutação, 412.365, vai estar na posição 362. 0385e1fde6054c463036bd7ea2ddf9ed3ba43851 Exemplo 4.6.5 - Solução 0 123 483 2015-12-10T03:34:32Z Igorolivei 26 Created page with "'''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em po..." wikitext text/x-wiki '''Solução:''' Existem 4! = 24 permutações de 1, 2, 3, 4, 5 que começam com 1; estas permutações estão em posições de 1 a 24. Da mesma forma, as permutações em posições 25 a 48 começam com 2 e as permutações em posições 49 através de 72 começam com 3 . Assim, a primeira permutação começando com 4, 41235, está na posição 73. Por conseguinte 41253 está na posição 74. cb1c02d99a6493507e008acbc4ba1fdba78db9cd Somatório e Produtório 0 82 486 456 2015-12-10T03:39:21Z Francleidepsimao 22 /* Exemplo 6 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde n ∈ N, com n ≥ 1. Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> da5bbb3379115ff2f38807de28376ed9adc6c1b1 487 486 2015-12-10T03:41:54Z Francleidepsimao 22 /* Exemplo 5 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> c564e7872534c84ade50836c3992f019bc7869f5 488 487 2015-12-10T04:18:49Z Francleidepsimao 22 /* Exemplo 5 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética: <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> 0f7572ebeb334b2fa9d15afc38005b62e10fa99e 489 488 2015-12-10T05:20:16Z Francleidepsimao 22 /* Exemplo 4 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> e35179867757674d1cb05a466fd25051353128f6 490 489 2015-12-10T05:47:12Z Francleidepsimao 22 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . Observe o padrão utilizado para resolver as duas questões anteriores e ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Francleide </pre> 6d918076b90f337056f29845b2f9bdc027d852a2 491 490 2015-12-10T05:50:08Z Francleidepsimao 22 /* Exemplo 5 */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Francleide </pre> 1db4ef2e1ca4aa6a6775d9ced0c6ded9477270c0 492 491 2015-12-10T05:50:54Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Francleide </pre> ddc0a18a6b27bcd4b3158377ecc9b8c817d7b029 493 492 2015-12-10T05:51:29Z Francleidepsimao 22 /* Resolução */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Francleide </pre> 40e1b44af7dc6aca818a884f35d69e5081dbdaa1 494 493 2015-12-10T05:52:08Z Francleidepsimao 22 /* Autores */ wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> 97496c4c8f76abcb4dc62135e44b09f2f4dca11d 495 494 2015-12-10T06:15:23Z Francleidepsimao 22 wikitext text/x-wiki O somatório representa somas com <math>n</math> termos, para sua representação utiliza-se o símbolo sigma <math>\sum_{n}{i=0} I </math> onde i representa o termo inicial da soma e n o termo final da soma. Ele geralmente é utilizado na resolução de problemas de recorrência. == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> 9b68a232bc99cabea7d7e4da4e61aaa02a66ad7c 496 495 2015-12-10T06:16:23Z Francleidepsimao 22 wikitext text/x-wiki O somatório representa somas com <math>n</math> termos, para sua representação utiliza-se o símbolo sigma <math>\sum_{i=0}^{n} i </math> onde i representa o termo inicial da soma e n o termo final da soma. Ele geralmente é utilizado na resolução de problemas de recorrência. == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> fed7aa8a95cf8b316ed86a9d13de920746b58bab 497 496 2015-12-10T06:17:12Z Francleidepsimao 22 wikitext text/x-wiki O somatório representa somas com <math>n</math> termos, para sua representação utiliza-se o símbolo sigma <math>\sum_{i=0}^{n} i </math> onde i indica o termo inicial da soma e n o termo final. == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> 826da2a7b5e180ed6c66fbce1fa5d71b410323eb 498 497 2015-12-10T06:18:24Z Francleidepsimao 22 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> média aritmética de <math>n-1 \text{: }\frac{1}{n-1}\sum_{k=1}^{n-1} k = 16,1</math> <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}=1,732050807568877</math> <math>\frac {\sqrt{1}+\sqrt{5}}{2}= 1,618033988749895 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}= 1,707106781186548 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> 97496c4c8f76abcb4dc62135e44b09f2f4dca11d 499 498 2015-12-10T12:01:39Z Francleidepsimao 22 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> Pela propriedade da progressão aritmética <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <math>\sum_{k=1}^{n} k = \frac {n(n-1)}{2}</math> <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math> Temos: <math>\sum_{k=1}^{n} k = \frac{n(n+1)}{2}</math> e teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}\simeq1,7</math> inserindo os valores na equação: <math>\frac {\sqrt{1}+\sqrt{5}}{2}\simeq1,6 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}\simeq 1,7 </math> <math>\frac {\sqrt{2}+\sqrt{5}}{2} \simeq 1,8 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> c95fb005fc6bc913500588185a502775e9e38ba3 500 499 2015-12-10T12:55:16Z Francleidepsimao 22 wikitext text/x-wiki == Propriedades de Somatório == <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. <math> \sum_{n=s}^t f(n) + \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) + g(n)\right] </math> <math> \sum_{n=s}^t f(n) - \sum_{n=s}^{t} g(n) = \sum_{n=s}^t \left[f(n) - g(n)\right] </math> <math> \sum^n_{i = m} f(i) = \sum^{n+p}_{i = m+p} f(i-p) </math> <math> \sum\limits_{n=s}^{t} j = \sum\limits_{n=1}^{t} j - \sum\limits_{n=1}^{s-1} j </math> <math> \sum_{n=s}^j f(n) + \sum_{n=j+1}^t f(n) = \sum_{n=s}^t f(n)</math>, note que <math> s \leq j \leq t </math> <math> \sum_{i=m}^n i = \frac{n(n+1)}{2} - \frac{m(m-1)}{2} = \frac{(n+1-m)(n+m)}{2},</math> progressão aritmética. <math> \sum_{i=0}^n i = \sum_{i=1}^n i = \frac{n(n+1)}{2} </math> <math> \sum\limits_{k=0}^{n-1}{2^k} = 2^n-1 </math> <math> \sum_{i=s}^m\sum_{j=t}^n {a_i}{c_j} = \sum_{i=s}^m a_i \cdot \sum_{j=t}^n c_j </math> <math> \sum_{i=0}^n i^3 = \left(\sum_{i=0}^n i\right)^2 </math> <math> \sum_{i=m}^{n-1} a^i = \frac{a^m-a^n}{1-a} (m < n) </math> <math> \sum_{i=0}^{n-1} a^i = \frac{1-a^n}{1-a} </math> ---- == Principais representações == ====Soma simples==== <math>\sum_{i=1}^{n} x_i = x_1+x_2+...+x_n</math> ====Soma de quadrados==== <math>\sum_{i=1}^{n} x_i^2 = x_1^2+x_2^2+...+x_n^2</math> ====Quadrado da soma==== <math>(\sum_{i=1}^{n} x_i)^2 = (x_1+x_2+...+x_n)^2</math> ====Soma de produtos==== <math>\sum_{i=1}^{n} x_iy_i = x_1y_1+x_2y_2+...+x_ny_n</math> ====Produtos das somas==== <math>(\sum_{i=1}^{n} x_i)(\sum_{j=1}^{m} y_j) = (x_1+x_2+...+x_n)(y_1+y_2+...+y_n)</math> ---- == Aplicação das Propriedades == Alguns exemplos de aplicações das propriedades do somatório: === Exemplo 1 === Utilize as propriedades de notação de somatório e, possivelmente, mudança de índice para deduzir que <math>\sum_{j=1}^n (a_j - a_{j-1})</math> é igual a <math>a_n - a_0</math>, onde <math>(a_i )_{i=0}^{\infty}</math> é uma sequência de números reais. Este tipo de soma é bastante conhecida em Matemática como ''soma telescópica''. ==== Resolução ==== <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + \sum_{j=1}^{n-1}</math> Expandindo <math>n</math> vezes: <math> \sum_{j=1}^n (a_j - a_{j-1}) = (a_n - a_{n-1}) + (a_{n-1} - a_{n-2}) + ... + (a_2 - a_1) + (a_1 - a_0)</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - \cancel{a_{n-1}} + \cancel{a_{n-1}} - \cancel{a_{n-2}} + ... + \cancel{a_2} - \cancel{a_1} + \cancel{a_1} - a_0</math> <math> \sum_{j=1}^n (a_j - a_{j-1}) = a_n - a_0 </math> === Exemplo 2 === O objetivo deste problema é encontrar uma fórmula fechada para <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> Para tal, note que <math>k^2 - ( k - 1)^2 = 2k - 1</math> Logo, <math>\sum_{k=1}^n \left ( k^2 - ( k - 1)^2 \right ) = \sum_{k=1}^n ( 2k - 1) </math> Então, utilize o resultado do problema conhecido como "soma telescópia" do exemplo 1 para encontrar a fórmula desejada. ==== Resolução ==== <math> \sum_{k=1}^n 2 \cdot k - 1 = \sum_{k=1}^n k^2 - (k-1)^2</math> Pela fórmula da soma telescópica <math> \sum_{k=1}^n 2 \cdot k - 1 = n^2 - 0^2 = n^2</math> === Exemplo 3 === Utilize as propriedades de notação de somatório e os seus conhecimentos de soma de termos de uma PA para calcular <math>\sum_{k=1}^n (2 \cdot k - 1 )</math> de forma distinta daquela usada no problema anterior. Qual das duas soluções lhe parece mais fácil? ==== Resolução ==== <math>\sum_{k=1}^n (2 \cdot k - 1 ) = \sum_{k=1}^n 2 \cdot k - \sum_{k=1}^n 1</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \sum_{k=1}^n k - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = 2 \cdot \frac{n \cdot (n+1)}{2} - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2 + n - n</math> <math>\sum_{k=1}^n (2 \cdot k - 1 ) = n^2</math> ===Exemplo 4=== Suprimindo um dos elementos do conjunto {<math>1, 2, . . . , n</math>}, a média aritmética dos elementos 16,1. Determine o valor de n e qual foi o elemento suprimido do conjunto para o cálculo da média. ==== Resolução ==== <math>\sum_{k=1}^{n} k = 1+2+...+n= \frac{n \cdot (n+1)}{2}</math> média aritmética é dada por : <math>\frac {\frac{n \cdot (n+1)}{2}}{n} = \frac{n \cdot (n+1)}{2n} = \frac{1}{n}\sum_{k=1}^{n} k</math> Pela propriedade da progressão aritmética <math>\sum_{k=1}^{n-1} k = \frac{(n-1)n}{2}</math> usando a função de calculo da média: <math>\frac{(n-1)n}{2(n-1)} = \frac{n}{2} = 16,1</math> <math>n = 32,2</math> Substituindo <math>n</math> na equação: <math>n-1 = 31,2</math> <math>\sum_{k=1}^{n-1} k = 517,92</math> <math>\sum_{k=1}^{n} k = 534,52</math> Portanto o termo omitido foi: <math>534,52 - 517,92 = 16,6</math> ===Exemplo 5=== Encontre uma fórmula fechada <math>\sum_{k=1}^{n} k^3</math> onde <math>n \in N \text{, com } n \geq 1</math> . ==== Resolução ==== <math>\sum_{k=1}^{n} k^3 = \sum_{k=1}^{n} kk^2 = \sum_{k=1}^{n} k \sum_{k=1}^{n} k^2</math> Temos: <pre>Incompleto </pre> ===Exemplo 6=== Calcule a soma <math>\sum_{k=1}^{n} k\cdot k!</math> onde <math> n \in N \text{,com } n \geq 1</math> ==== Resolução ==== Separando o somatório: <math>\sum_{k=1}^{n} k\cdot k! =\sum_{k=1}^{n} k\cdot \sum_{k=1}^{n} k! </math>/ teremos que descobrir o <math>\sum_{k=1}^{n} k!</math> então <math>\sum_{k=1}^{n} k!+(n+1)! = \sum_{k=0}^{n} (k!+1)! </math> <math>1+\sum_{k=1}^{n} (k+1)! = 1+\sum_{k=1}^{n} (k+1)k!</math> <pre>Incompleto </pre> ===Exemplo 7=== Os números <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> podem pertencer a uma mesma progressão aritmética? ==== Resolução ==== Assumindo uma PA <math>(\sqrt{1},\sqrt{2}, \sqrt{3}, \sqrt{4},\sqrt{5})</math> os termos <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> pertencem a essa progressão se pela propriedade da progressão aritmética a média aritmética dos termos da ponta de uma sequencia (a, b e c) for igual a o termo do meio: <math>\frac {a+c}{2}= b </math> <math>\sqrt{3}\simeq1,7</math> inserindo os valores na equação: <math>\frac {\sqrt{1}+\sqrt{5}}{2}\simeq1,6 </math> <math>\frac {\sqrt{2}+\sqrt{4}}{2}\simeq 1,7 </math> <math>\frac {\sqrt{2}+\sqrt{5}}{2} \simeq 1,8 </math> Portanto <math>\sqrt{2}, \quad \sqrt{3} \quad \text{e} \quad \sqrt{5}</math> não pertencem a mesma progressão aritmética. ---- == Provas de algumas propriedades == ===Multiplicação por constante=== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante. ===== Passo base: s = t ===== <math> \sum_{n=s}^t C\cdot f(n) = C\cdot f(n) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k C\cdot f(n) = C\cdot \sum_{n=s}^k f(n) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + \sum_{n=s}^k C\cdot f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot f(k+1) + C\cdot \sum_{n=s}^k f(n)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1)) + C\cdot (f(k) + f(k-1) + ... + f(s+1) + f(s))</math> Colocando <math>C</math> em evidência: <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot (f(k+1) + f(k) + f(k-1) + ... + f(s+1) + f(s))</math> <math> \sum_{n=s}^{k+1} C\cdot f(n) = C\cdot \sum_{n=s}^{k+1} f(n) </math> Portanto: <math> \sum_{n=s}^t C\cdot f(n) = C\cdot \sum_{n=s}^t f(n) </math>, onde C é uma constante, <math>\forall s, t \in N</math>. === Mudança de índices === <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math> ===== Passo base: s = t ===== <math> \sum_{n=s}^t f(n) = f(n) = \sum_{n=s+1}^{t+1} f(n-1) </math>, pela definição de somatório. ===== Passo indutivo: s < t ===== Suponha que para um <math>k \in N, k > s</math> arbitrário: <math> \sum_{n=s}^k f(n) = \sum_{n=s+1}^{k+1} f(n-1) </math> (Hipótese de indução) Para <math>k+1</math>, assumindo o lado esquerdo da equação, temos: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s}^k f(n)</math>, pela definição de somatório. Aplicando a HI: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + \sum_{n=s+1}^{k+1} f(n-1)</math> Expandindo <math>k-s</math> vezes: <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k+1-1) + f(k-1) + ... + f(s-1) + f(s+1-1)</math> <math> \sum_{n=s}^{k+1} f(n) = f(k+1) + f(k) + f(k-1) + ... + f(s-1) + f(s)</math> <math> \sum_{n=s}^{k+1} f(n) = \sum_{n=s+1}^{k+2} f(n-1)</math>, uma vez que existem <math>k+2</math> termos. Portanto: <math> \sum_{n=s}^t f(n) = \sum_{n=s+1}^{t+1} f(n-1) \forall s, t \in N</math>. ---- == Somatório em Linguagem Funcional == ====Elixir<ref>https://github.com/jaimerson/fmc-elixir-somatorio</ref>==== <pre> defmodule FMC do def somatorio(start \\0, finish, callback) def somatorio(start, finish, callback) when start == finish do callback.(start) end def somatorio(start, finish, callback) do _somatorio(Enum.to_list(start..finish), callback) end defp _somatorio([], _), do: 0 defp _somatorio([head | tail], callback) do callback.(head) + _somatorio(tail, callback) end end </pre> ---- ==Referências== <references /> ---- ==Autores== <pre>Jaimerson Araújo Francleide Simão </pre> bd9482ee7e82c96009b9f3b6c3075824c5887956 Técnicas Avançadas de Contagem 0 81 501 276 2016-03-10T11:20:28Z Marcielmanoel15 30 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. =='''Relações de recorrência'''== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ===''' Torre de Hanoi '''=== O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. =='''Resolução de recorrências com Maple'''== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ==='''Uma relação de recorrência linear homogênea com coeficientes constantes'''=== Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); ==='''Relações de recorrência heterogêneas'''=== Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N = -1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. ==='''Resolvendo recorrências em Maple'''=== Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math> r_ {n} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); ==='''Relações de dividir e conquistar'''=== Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_n = a \cdot r_{n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); =='''Inclusão – Exclusão'''== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; =='''Funções geradoras'''== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math>{\ r_{n} \}</math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> =='''Cálculos e como explorá-los'''== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); ==='''2. Encontrar tantas números de Fibonacci primos que puder.'''==== '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. ==='''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.'''=== '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); =='''Exemplos Extras'''== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é <math>c= 3</math> e <math>d = -\frac{11}{5}</math>. Assim, a solução para a relação de recorrência é <math>a_n = 3 . 5^n - \frac{11}{5} . n . 5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c - 3 = 0</math> <math>d + 8c – 8d – 24c + 12d = 0</math> ou <math>5c – 3d = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo <math>a = \frac{27}{255}</math> e <math>b = -2</math>. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> 21c2ad0e7083b1329a0fde2f7a5369601308b173 502 501 2016-03-10T12:29:40Z Marcielmanoel15 30 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. =='''Relações de recorrência'''== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ===''' Torre de Hanoi '''=== O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. =='''Resolução de recorrências com Maple'''== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ==='''Uma relação de recorrência linear homogênea com coeficientes constantes'''=== Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); ==='''Relações de recorrência heterogêneas'''=== Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N = -1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. ==='''Resolvendo recorrências em Maple'''=== Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math> r_ {n} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); ==='''Relações de dividir e conquistar'''=== Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_n = a \cdot r_{n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); =='''Inclusão – Exclusão'''== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; =='''Funções geradoras'''== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math> r_{n} </math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> =='''Cálculos e como explorá-los'''== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); ==='''2. Encontrar tantas números de Fibonacci primos que puder.'''==== '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. ==='''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.'''=== '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); =='''Exemplos Extras'''== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é <math>c= 3</math> e <math>d = -\frac{11}{5}</math>. Assim, a solução para a relação de recorrência é <math>a_n = 3 . 5^n - \frac{11}{5} . n . 5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math>n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math>c – 8c + 12c - 3 = 0</math> <math>d + 8c – 8d – 24c + 12d = 0</math> ou <math>5c – 3d = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo <math>a = \frac{27}{255}</math> e <math>b = -2</math>. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> 788b3be2cf1bcaccb6b9e9c965471319defb45e4 503 502 2016-03-10T12:31:32Z Marcielmanoel15 30 wikitext text/x-wiki Neste capítulo vamos descrever como usar Maple para trabalhar com três temas importantes na contagem: relações de recorrência, inclusão-exclusão e funções geradoras. Começaremos por descrever como Maple pode ser usado para resolver relações de recorrência, incluindo a relação de recorrência para a sequência de números de Fibonacci. Em seguida, mostraremos como resolver o enigma da Torre de Hanoi e encontramos o número de movimentos necessários para n discos. Descreveremos como Maple pode ser utilizada para resolver as relações lineares homogêneas de recorrência com coeficientes constantes, bem como as relações de recorrência não homogêneas relacionadas. Depois de descrever como resolver estes tipos especiais de relações de recorrência com Maple, vamos mostrar como usar a resolução geral de recorrência Maple. Nós ilustramos o uso dessa resolução geral demonstrando como usá-la para resolver relações de recorrência com o método “Divide and Conquer”. Depois de estudar relações de recorrência, vamos mostrar como usar Maple para ajudar a resolver problemas usando o princípio da inclusão e exclusão. Ao fim, discutiremos como Maple pode ser usado para trabalhar com funções geradoras, um tema abordado no Apêndice 3 no texto. =='''Relações de recorrência'''== Uma relação de recorrência descreve uma relação que um membro de uma sequência {<math>{a_n}</math>} de valores tem de outro membro da sequência que o precedem. Por exemplo, a famosa sequência de Fibonacci {<math>F_n</math>} satisfaz a relação de recorrência <math>F_{(n)} = F_{(n-1)} + F_{(n-2)}</math> Juntamente com as condições iniciais <math>F_1 = 1 </math> e <math>F_2 = 1 </math>, esta relação é suficiente para definir toda a sequência <math>F_n</math> Em geral, podemos pensar em uma relação de recorrência como uma relação do formulário <math>r_{n} = f(r_{n-1}, r_{n-2}, \ldots , r_{n-k})</math>, em que cada termo <math>r_{n}</math> da sequência depende de um número k dos termos que o precedem. Por exemplo, para a sequência de Fibonacci, a função F é <math>f(x, y) = y + x</math>. Para entender como podemos trabalhar com relações de recorrência em Maple, temos de parar por um momento e perceber que uma sequência <math>r_{n}</math> de valores (números, matrizes, círculos, funções, etc.) é apenas uma função cujo domínio passa a ser o conjunto de inteiros (geralmente positivos). Se queremos levar este ponto de vista (e nós queremos!), então o <math>r</math> enésimo termo <math>r_{n}</math> de uma sequência de {<math>r_{n}</math>} seria convencionalmente escrito como <math>r_(n)</math>, e gostaríamos de referir à função r. Desta forma, podemos pensar na sequência {<math>r_ {n}</math>} como uma forma de representar uma função <math>r</math> cujo domínio é o conjunto de números inteiros positivos, e cujo valor no número <math>n</math> é apenas <math>r_{n} = r(n)</math>. Isto apenas equivale a uma mudança na notação; não há nada mais do que isso. Uma vez que esta alteração na notação for feita, então é fácil ver como representar uma relação de recorrência como um procedimento Maple tendo argumentos inteiros. No capítulo 3 descobrimos como representar de forma eficiente a sequência de Fibonacci pelo procedimento: <pre>Fibonacci := proc(n::posint) option remember; if n = 1 or n = 2 then RETURN( 1 ); fi; Fibonacci2(n-1) + Fibonacci2(n-2);</pre> Lembre-se que a primeira linha deste procedimento instrui a Maple de lembrar que quaisquer sejam os valores do processo já foram calculado na sessão atual. Às vezes, apesar de nossos melhores esforços, uma aplicação recursiva de um algoritmo pode ser muito caro, simplesmente devido à sua própria natureza. A aplicação recursiva pode ser evitado se podemos encontrar uma fórmula explícita para o termo geral da recorrência. O processo de encontrar uma fórmula explícita é referido como resolver a recorrência. Na próxima seção, veremos como usar Maple para fazer isso por certos tipos de relações de recorrência. ===''' Torre de Hanoi '''=== O famoso enigma conhecido como Torre de Hanoi é discutido no texto, onde a relação de recorrência: <math>H_{n} = 2H_{n - 1} + 1, H_{1} = 1</math> é derivada, em que <math>H_{n}</math> indica o número de movimentos necessários para resolver o enigma para n discos. Como discutido no texto, este tem a solução <math>H_n = 2 ^{n} - 1</math> Mais tarde, veremos como usar Maple para obter esse resultado de forma simples. Além de resolver para o número de moviem Maplementos necessários para resolver o enigma das Torres de Hanoi para para n discos, podemos ilustrar a solução escrevendo um programa em Maple para calcular os movimentos necessários para resolver o dito problema, que, posteriormente, os descreverá. Nós vamos escrever um pequeno programa em Maple que consiste em três procedimentos: o principal programa de '''Hanoi''', a rotina utilitária '''PrintMove''', e o mecanismo recursivo do programa '''TransferDisk''', que faz a maioria do trabalho. A parte mais fácil de escrever é a função '''PrintMove''', que apenas mostra para nós a mudança para fazer em um determinado passo. PrintMove: = proc (src :: string, dest :: string) printf (`Mova disco de peg% s para peg% s`, src, dest); end: Aqui, nós apenas chamamos o comando '''printf''' da biblioteca do Maple, que pode ser usado para saída formatada. A função '''printf''' tem uma sintaxe de chamada complexa; consulte a ajuda online para obter detalhes e informações adicionais. (Nota: Se você estiver familiarizado com a função printf em C, então você vai achar que a versão do Maple do printf é bem semelhante. Neste caso, os símbolos %s acima são substituídos pelos valores de string do segundo e terceiro argumentos, respectivamente.) Em seguida, o procedimento recursivo '''TransferDisk''' faz a maior parte do trabalho para nós. Esta função modela a ideia de transferir um disco de um pino para outro. Mas, uma vez sendo recursivo, precisamos fornecer a ele, como um argumento, o número total de discos a serem tratados em cada chamada. TransferDisk := proc(src::string, via::string, dest::string, ndisks::posint) if ndisks = 1 then PrintMove(src, dest); else TransferDisk(src, via, dest, ndisks -1); PrintMove(src, dest); TransferDisk(via, dest, src, ndisks -1); fi; end: Finalmente, compilamos como um procedimento de alto nível, '''Hanoi''', proporcionando assim uma interface para o mecanismo recursivo. Hanoi := proc(ndisks::posint) if ndisks < 1 then printf(`What's wrong with this picture?`); else TransferDisk(`A`, `B`, `C`, ndisks); fi; end: Nosso programa '''Hanoi''' consegue exibir uma solução específica para o Enigma das Torres de '''Hanoi''' para qualquer número '''ndisk''' de discos. Hanoi(2); Hanoi(3); Tente experimentar com diferentes valores '''ndisk''' para ter uma noção do quão grande o problema se torna mesmo para valores moderadamente grandes de '''ndisks'''. =='''Resolução de recorrências com Maple'''== Agora que sabemos como implementar relações de recorrência em Maple, e temos trabalhado com eles um pouco, vamos ver como usar Maple para resolver certos tipos de relações de recorrência. Maple tem um poderoso mecanismo solucionador de recorrência, rsolver, que discutiremos mais tarde. A sua utilização, no entanto, pode obscurecer algumas das ideias importantes que estão envolvidas. Portanto, devemos primeiro usar algumas das instalações mais rudimentares do Maple para resolver certos tipos de relações de recorrência, um passo de cada vez. Dada uma sequência definida recursivamente <math> {r_ {n}} </math>, o que nós gostaríamos é encontrar algum tipo de fórmula, envolvendo apenas o índice n (e, talvez, outras constantes fixas e funções conhecidas) que não dependem do conhecimento da valor de <math>r_{k}</math>, por qualquer índice k. Para começar, vamos considerar relações de recorrência que são lineares, homogêneas, e que têm coeficientes constantes; ou seja, eles têm a forma <math> r_{n} = a_{1}r_{n-1} + a_{2}r_{n-2} + \cdots + a_{k}r_{n-k} </math> onde <math> a_{1}, a_{2}, \ldots , a_{k} </math> são constantes reais e <math> a_{k} </math> é diferente de zero. Lembre-se que o inteiro k é chamado de grau da relação de recorrrência. Para ter uma única solução, pelo menos o k inicial deve sere especificado. O método geral para resolver tal relação de recorrência envolve encontrar as raízes de seu polinômio característico. <math> x^{k} - a_{1}x^{k-1} - a_{2}x^{k-2} - \cdots - a_{k-1}x - a_{k} </math> Quando este polinômio tem raízes distintas, todas as soluções são combinações lineares das enésimas (palavra para número ordinal) potências dessas raízes. Quando não são raízes repetidas, a situação é um pouco mais complicado, como veremos. Para começar, vamos considerar uma relação de recorrência linear homogênea com coeficientes constantes de grau dois: <math> r_{n} = 2r_{n-1} + 3r_{n-2} </math> sujeitos às condições iniciais <math> r_{1} = 4 </math> and <math> r_{2} = 2 </math> Então sua equação característica é: <math> x^{2} - 2x - 3 </math> Para resolver a relação de recorrência, temos de resolver para as raízes dessa equação. Usar Maple faz disso algo muito fácil; nós usamos a função solve para fazer isso. solve (x^2 - 2 * x - 3 = 0, x); A sintaxe diz à função que queremos os valores da variável x que '''satisfazem''' a equação quadrática. '''<math> x^2 - 2 * x - 3 = 0. </math>''' Agora que o Maple aponta que as soluções são <math>x = 3</math> e <math>x = -1</math>, podemos escrever a forma de a solução para a recorrência como <math> r_ {n} = \alpha 3 ^ {n} + \beta (-1) ^ {n} </math> onde \alpha e \beta são constantes que ainda temos de determinar. Podemos usar Maple para determinar as constantes <math>\alpha</math> e <math>\beta</math>. Uma vez que as condições iniciais são <math> r_ {1} = 4 e r_ {2} = 2 </math>, sabemos que a nossa relação de recorrência deve satisfazer as seguintes duas equações. <math> 3\alpha - \beta = 4 </math> <math> 3^{2}\alpha + \beta = 2 </math> ==='''Uma relação de recorrência linear homogênea com coeficientes constantes'''=== Agora vamos generalizar o que temos feito e escrever um procedimento em Maple para resolver uma relação de recorrência geral homogênea com coeficientes constantes, de grau 2, considerando que as raízes do polinômio característico da relação de recorrência são distintos. Vamos escrever um procedimento RecSol2 que resolve a recorrência <math> r_{n} = ar_{n-1} + br_{n-2} </math> sujeito às condições iniciais <math> r_{1} = u and r_{2} = v </math> e, em seguida, retorna um procedimento que pode ser utilizado para calcular termos da sequência. Por enquanto, suponha que o polinômio característico <math> x^{2} - ax - b </math> tem duas raízes distintas. Então, tudo o que o nosso procedimento precisa fazer é repetir os passos que fizemos manualmente no nosso exemplo anterior. RecSol2 := proc(a, b, u, v) local evals, S, alpha, beta, ans , n; Resolve-se a equação característica evals := solve(x^2 - a * x - b = 0, x); Depois, resolve-se o sistema de equações lineares S := solve(alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v, alpha,beta); ans := subs(S,alpha*evals[1]^n + beta*evals[2]^n); RETURN( unapply( ans , n ) ); end: Para observar como funciona, iremos tentar alguns casos de teste. De modo a construir uma função para calcular a sequencia Fibonacci, chamamos nosso novo procedimento: f := RecSol2(1,1,1,1,5); O procedimento resultante pode ser usado para calcular o termo geral da sequencia Fibonacci. f(n); Da mesma forma, os primeiros cinco números Fibonacci podem ser calculados da seguinte forma: seq(simplify(f(n)), n = 1..10); Agora apresentamos uma resolução que pode lidar com o caso de raízes repetidas. Antes de olharmos para a nova versão do RecSol2, vamos olhar para um exemplo envolvendo uma relação de recorrência com um valor double próprio (raiz de seu polinômio característico). A relação de recorrência <math> r_n = 4r_{(n-1)} - 4r_{(n-2)} </math> tem a equação característica char_eqn := x^2 - 4 * x + 4 = 0; com autovalor evals := [solve(char_eqn, x)]; No geral, para testar um autovalor repetido, que é o caso para este exemplo, apenas testamos se evalb(evals[1] = evals[2]); (Nota: Nós não requeremos o uso de EVALB em uma instrução condicional já que expressões são automaticamente avaliados como booleans.) Se chamarmos a raiz dupla (2 neste caso) <math> \ lambda </math>, então a relação de recorrência tem a solução explícita <math> r_{n} = \alpha \lambda^{n} + n \beta \lambda^{n} </math> para todos os positivos inteiros n, e para algumas constantes <math> \alpha </math> and <math> \beta </math>. Assumir as condições iniciais de <math> r_{1} = 1 and r_{2} = 4 </math>, o conjunto S de equações a resolver é: S := alpha * evals[1] + beta * evals[2] = 1, alpha * evals[1]^2 + 2* beta * evals[2]^2 = 4; Como antes, para obter as soluções, digitamos: rsols := solve(S, alpha, beta); É neste ponto que a diferença com o caso de raízes distintas aparece. O enésimo termo da sequência, quando há um double autovalor, é dado por: subs(rsols , alpha * evals[1]^n + n * beta * evals[1]^n ); Os passos feitos neste exemplo são bem gerais Um procedimento geral para resolver uma recorrência de dois termos da forma r(n) = a r(n-1) + b r(n-2), com os valors iniciais values r(1) = u and r(2) = v é: RecSolver2 := proc(a,b,u,v) local ans, evals, S, alpha, beta, rsols, n; resolve a equação característica evals := solve(x^2 - a * x - b = 0, x); resolve o sistema de equações lineares S := alpha * evals[1] + beta * evals[2] = u, alpha * evals[1]^2 + beta * evals[2]^2 = v; rsols := solve(S, alpha, beta); if evals[1] = evals[2] then # repeated roots ans := subs(rsols,alpha*evals[1]^n + beta*n*evals[1]^n); else ans := subs(rsols,alpha*evals[1]^n + beta*evals[2]^n ); fi; RETURN( unapply(ans , n ) ); end: Esta versão da nossa resolução testa primeiro raízes repetidas, e então faz o cálculo apropriado baseado no resultado. É chamado da mesma forma que o RecSol2: g := RecSolver2(4,-3,1,2); i :='i': seq(simplify(g(i)), i=1..10); Isto dá os dez primeiros termos da sequência definida pela relação de recorrência <math> r_{n} = 4r_ {N-1} - 3 * r_ {N-2} </math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>. Para resolver a recorrência <math> r_{n} = {N -r_-1} - r_ {N-2}</math>, com condições iniciais <math> r_{1} = 1 e R_ {2} = 2 </math>, usamos A solução e os primeiros 100 termos desta sequência são h := RecSolver2(-1,-1,1,2); i := 'i': seq(simplify(h(i)),i=1..10); Perceba que o padrão que aparece se substituirmos as condições iniciais <math> r_{1} = 1 and r_{2} = 2 </math> com constantes simbólicas. k := RecSolver2(-1, -1, lambda, mu); i := 'i': seq(simplify(k(i)),i=1..10); ==='''Relações de recorrência heterogêneas'''=== Nós temos, até agora, discutido relações de recorrência lineares homogêneas com coeficientes constantes. No entanto, as técnicas utilizadas para resolvê-los podem ser estendidas para fornecer soluções para as recorrências heterogêneas deste tipo. Estas são relações de recorrência da forma <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = c_{n}</math> onde <math> \alpha_{n}, \alpha_{n-1}, \ldots, \alpha_{n-k}</math> e <math>c_{n} </math> são constantes. A única nova problemática é que, aqui, o <math>c_{n}</math> não precisa ser zero. Dito de outra forma, uma equação desta forma, na qual cada <math>c_{n}</math> é zero é homogênea, por isso as relações homogêneas são apenas um caso especial deste tipo mais geral. Para resolver a recorrência mais geral, precisamos fazer duas coisas: 1) Encontrar uma solução específica para a recorrência heterogênea; 2) Resolver a recorrência homogênea correspondente. A recorrência homogênea correspondente é apenas a obtida substituindo a sequência <math> c_ {{n}} </math> pela sequência zero: <math>\alpha_{n}r_{n} + \alpha{n-1}r_{n-1} + \cdots + \alpha{n-k}r_{n-k} = 0 </math> Então, nós já sabemos como fazer o segundo passo. O primeiro passo é mais difícil, mas com a ajuda do Maple, ele manejável. rSolve (r(0) = 0, r (n) = 3 * r (n-1) + 3 ^ N, r (n)); normal (%, expanded); Isso nos diz que <math>r_{n} = n3^n </math> é uma solução para a relação de recorrência <math>r_{n} = 3r_{n-1} + 3^n</math>. Agora, todas as soluções são obtidos por adição de uma solução para este conjunto de soluções da recorrência homogênea correspondente. rSolve (r(n) = 3 * r (n-1), r (n)); % + * 3 N ^ N; Se temos um valor inicial para <math>r_{0}</math>, então nós temos uma solução completa. Agora vamos resolver a Torre de Hanoi <math>H_n = 2H_{n-1} + 1</math> o que dá o número de movimentos necessários para resolver o enigma da Torres de Hanoi com n discos. Lembre-se que <math>H_{1} = 1</math>. A relação de recorrência homogênea associada é <math>h_{n} = 2 h_{n - 1}</math> com polinômio característico <math>x - 2</math> A única raiz disso é 2, portanto, todas as soluções da relação de recorrência homogênea têm a forma <math>h_{n} = \alpha 2 ^ {n - 1}</math> para alguma constante <math>/alfa </math>. (A potência de 2 é N-1, em vez de N, porque a recorrência começa no 1 em vez de 0.) Soluções para a H são obtidos a partir das soluções para h por adição de uma solução particular para H. Agora, H tem a solução constante <math>H_ N = -1</math>, para todo n, então todas as soluções para a H são da forma <math>H_ {n} = /alpha 2 ^ {n} - 1</math> Usando a condição inicial <math>H_ {1} = 1</math> podemos resolver para <math>/alpha</math> como se segue. solve (alfa * 2 ^ 1 - 1 = 1, alfa); Assim, a solução para as Torres de Hanoi é <math>H_ {n} = 2 ^ {n-1} - 1</math>. ==='''Resolvendo recorrências em Maple'''=== Agora que vimos como é possível usar Maple para implementar um algoritmo para resolver relações de recorrência simples, é hora de introduzir próprios utilitários do Maple para trabalhar com relações de recorrência. Já vimos o comando Maple solve para trabalhar com equações e sistemas de equações polinomiais. Da mesma forma, há um comando rSolve em Maple, que é especialmente projetado para lidar com relações de recorrência. É uma versão sofisticada de nosso procedimento RecSol2, que pode lidar com relações de recorrência de grau arbitrário, e pode lidar com raízes repetidas, bem como relações de recorrência não-lineares. Para usar rSolve, você precisa dizer a ele qual é a relação de recorrência, e algumas condições iniciais. Você também deve especificar o nome da função recursiva para resolver. Por exemplo, para resolver a recorrência Fibonacci, você pode digitar rSolve (f (n) = f (n-1) + f (n-2), F (0) = 0, f (1) = 1, f (n)); normal (%, expanded); Não é realmente necessário especificar as condições iniciais para uma relação de recorrência. Se eles não estiverem presentes, o Maple ainda vai resolver a equação, inserindo constantes simbólicas (aqui, G (0) e g (1)) em lugar das constantes numéricas, como o exemplo a seguir ilustra. rSolve (g (n) = 2 * g (n-1) - 6 * g (n-2), g (n)); Vemos, nesta fórmula, que Maple utiliza o símbolo I para denotar a unidade imaginária <math>(/sqrt {-1})</math>. A função rSolve pode lidar com vários tipos de diferenças de relações de recorrência. Em Maple V, Release 4, esta lista inclui: 1. relações de recorrência lineares com coeficientes constantes; 2. sistemas de relações de recorrência lineares com coeficientes constantes; 3. “Divide and Conquer” relações de recorrência com coeficientes constantes; 4. muitas relações de recorrência lineares de primeira ordem; 5. algumas relações de recorrência não-lineares de primeira ordem. As capacidades do rSolve, como outras funções do Maple, estão constantemente a serem melhoradas e ampliadas. Se você tiver uma versão posterior do Maple você pode achar que a sua versão do rSolve tem capacidades para além das enumeradas acima. No entanto, rSolve não é um algo mágico para resolver todos os problemas; você pode facilmente encontrar relações de recorrência que o rSolve é incapaz de resolver. Quando rSolve é incapaz de resolver uma relação de recorrência, ele simplesmente retorna “unevaluated”. Muitas vezes é o caso que um problema, tal como apresentado, não dá qualquer indicação de que uma solução pode ser encontrada usando recorrências. Vamos ver como podemos usar Maple para resolver um problema real; isto é, um que não esteja explicitamente expresso como um que exige a utilização de recorrência para a sua solução. Em quantas regiões é o plano dividido por 10000 linhas, assumindo que nenhuma das duas linhas são paralelas, e nenhuma das três são coincidentes? Tal situação pode ocorrer, numa tentativa de modelar fissuras no fundo do oceano, ou em qualquer outra parte da superfície da terra. Para começar, podemos tentar descobrir a resposta para um número menor de linhas. Assim, para generalizar o problema, poderemos perguntar o número de regiões produzidas por n linhas, onde n é um número inteiro positivo. É bastante óbvio que uma única linha (que corresponde ao caso em que n = 1) divide o plano em 2 regiões. Duas linhas, se não forem paralelas, pode ser facilmente vistas para dividir um plano em 4 regiões. (Duas linhas paralelas distintas produzem apenas três regiões.) Se chamarmos o número de regiões produzidas por n linhas, duas das quais são paralelas, e três das quais são coincidentes <math> r_{n}</math>, então temos <math>r_ {1} = 2</math> e <math>r_ {2} = 4</math>. Até agora, ele está começando a se parecer com <math>r_ {n} = n ^ {2}</math>. Mas não vamos ter pressa. O que acontece quando a situação se torna semelhante semelhante a n = 3? A figura mostrada aqui é representativa da situação. '''Figure''': file = ch05 / 3lines.eps Neste caso, o número <math>R_ {3}</math> das regiões é 7, de modo que a estimativa inicial que<math> R_ {n}</math> é <math>n ^ {2}</math> não pode ser certa. Para encontrar <math>r_ {4}</math>, temos de acrescentar uma quarta linha para o diagrama. Isto sugere tentar calcular <math>r_ {4}</math> em termos de <math>r_ {3}</math>, para que possamos pensar em <math> r_ {n} </math>como uma relação de recorrência. A figura mostra que a situação parece quando uma quarta linha é adicionada a três linhas existentes. '''Figure''': file = ch05 / 4lines.eps A partir dos pressupostos que nem duas das linhas podem ser paralelas e que nenhuma das três passam através de um único ponto, segue-se que a nova linha deve interceptar cada uma das três linhas existentes em exatamente um ponto. Isto significa que a nova linha passa através de exatamente três das regiões formadas pelas três linhas originais. Cada região que é atravessada é dividida em duas zonas, de modo que o número total de novas regiões adicionados através da adição da quarta linha é 3. Assim, <math>R_ {4} = r_ {3} + 3</math>. Argumentos semelhantes para uma configuração geral de linhas revelam que <math>R_ {n}</math> satisfaz a relação de recorrência <math>R_ {n} = N-R_ {1} + (n-1)</math> Além disso, já calculamos a condição inicial <math>r_ {1} = 2</math>. Este é o suficiente para resolver esta recorrência. rSolve (   r (n) = r (n-1) + (n-1),    R (1) = 2, r (n)); simplify(%); ==='''Relações de dividir e conquistar'''=== Um bom exemplo de relações de “Divide and Conquer” é a fornecida pelo algoritmo de busca binária. Aqui, vamos considerar uma aplicação prática deste algoritmo em uma implementação de uma busca binária em uma lista ordenada de números inteiros. O algoritmo procura por chave no IList. BinSearch := proc(ilist::list(integer), key::integer) local mid, lo, hi; hi := nops(ilist); lo := 0; while hi - lo > 1 do mid := floor((lo + hi) / 2); if key <= ilist[mid] then hi := mid; else lo := mid; fi; od; if ilist[hi] = key then RETURN(hi); else RETURN(false); fi; end: A variável '''IList''' é a lista de números inteiros para a busca, e o parâmetro '''key''' é o número inteiro para procurar. A posição na lista é retornada se ele for encontrado, e o valor '''false''' é retornado em caso contrário. Para testar '''Binsearch''', usamos o seguinte passo com uma lista de amostras para pesquisa. a := [3,5,7,12,34,546,5324,5346753]; for i in a do if a[BinSearch(a, i)] <> i then print(`Socks for President in '96!`); fi; od; Infelizmente para Socks, o nosso programa funcionou muito bem. Vamos agora fazer a análise do algoritmo para ver como relações de recorrência "Divide and Conquer" são geradas. Em geral, uma relação de recorrência do tipo dividir e conquistar tem a forma <math> r_n = a \cdot r_{n / K} + b </math> para algumas constantes a, K e b. Agora, a rotina Maple rSolve não tem absolutamente nenhuma dificuldade para lidar com até mesmo o tipo mais geral de relação dividir e conquistar. rSolve (r (n) = a * r (n / k) + b, r (n)); Se sabemos que, dado <math>r_ {1} = 4</math>, então podemos calcular Subs (R (1) = 4,%); Cada chamada para o algoritmo de busca binária produz listas a = 2, e cada um é metade do tamanho da lista original (k = 2). Portanto, o multiplicador e o período, no caso de um algoritmo de busca binária são ambos iguais a 2 e, portanto, obtemos Subs (a = 2, k = 2,%); Finalmente, se sabemos que b = 2, podemos calcular Subs (b = 2,%); simplify(%); =='''Inclusão – Exclusão'''== Nós vamos começar a ver, nesta seção, a segunda das duas principais técnicas de contagem abrangida no Capítulo 5 desse texto – O princípio de inclusão e exclusão. Vamos ver como usar Maple para resolver problemas com essa técnica. No cerne do princípio de inclusão e exclusão está a fórmula <math> | A \cup B | = | A | + | B | - | A \cap B | </math> a qual diz que, para dois conjuntos finitos A e B, o número de elementos da união AUB de dois conjuntos devem ser encontrados primeiramente ao adicionar os tamanhos |A| de A e |B| de B, e depois subtrair o numero de elementos comuns a ambos A e B, senão seria contado duas vezes. Esta fórmula pode ser generalizada para contar o número de elementos da união de qualquer número finito de conjuntos finitos. Para trabalhar com fórmulas como esta em Maple, é necessário aprender primeiro como representar conjuntos em Maple. Já que Maple é especialmente projetada para fazer matemática, isto é feito muito naturalmente: para representar um conjunto de elementos, simplesmente listamos estes elementos, separando-os por vírgulas, e incluindo toda a construção em chaves. Por exemplo, para representar o conjunto {2,3,5} cujos membros são os números 2, 3 e 5, nós podemos usar notação matemática comum. 2, 3, 5; Em Maple, um conjunto é a estrutura de dados de primeira classe. Você pode atribuir um conjunto a uma variável: A := 2, 3, 5; Perceba que a ideia do Maple de um conjunto corresponde precisamente a uma notação matemática. Assim, não existe uma ordem implícita entre os membros de um conjunto, nem existe qualquer noção de multiplicidade para membros do conjunto. Para problemas que requerem este tipo de informação adicional, outras estruturas de dados, como listas e arranjos, devem ser usadas. Podemos ver isso em Maple com os exemplos a seguir: A := `Alice`, `Bob`, `Eve`; B := `Bob`, `Alice`, `Eve`; evalb(A = B); C := `Alice`, `Bob`, `Eve`, `Eve`; evalb(A = C); O procedimento evalf avalia uma expressão booleana, e retorna verdadeiro ou falso, de acordo com a veracidade da falsidade da expressão. Então, Maple considera os três conjuntos A, B e C como o mesmo conjunto. O primeiro exemplo mostra que a ordem em que são listados os membros de um conjunto é irrelevante, enquanto o segundo mostra que, apesar de listar a string ‘Eve’ duas vezes, Maple só a vê uma vez. (Experimento com estes exemplos usando listas, que são delimitadas com colchetes em vez de chaves, para ver a diferença entre conjuntos e listas in Maple). Para determinar o tamanho de um conjunto (o número de objetos dentro dele) in Maple, usamos o procedimento Maple nops (pense nisso como n operandos) A := `Alice`, `Bob`, `Eve`; nops(A); C := `Alice`, `Bob`, `Eve`, `Eve`; nops(C); Os operadores teóricos de conjuntos (união) e (interseção) são representados em Maple pela escrita de seus nomes – union e intersect (em inglês), respectivamente. A := 1, 2, 3, 4, 5: B := 4, 5, 6, 7, 8: A union B; A intersect B; Além disso, a diferença teórica de conjuntos é denotada pelo operador Maple minus. A minus B; Vamos usar as operações para verificar o princípio de inclusão e exclusão em um exemplo particular. Flintstones := `Fred`, `Wilma`, `Pebbles`; Rubbles := `Barney`, `Betty`, `Bam Bam`; Husbands := `Fred`, `Barney`; Wives := `Wilma`, `Betty`; Kids := `Pebbles`, `Bam Bam`; Se este fosse um censo completo, então o número de crianças morando em Bedrock seria nops(Kids); enquanto que o número de habitantes de Bedrock que também são Flintstones ou criança é nops(Flintstones union Kids); De acordo com o princípio de inclusão e exclusão, este número também deveria ser nops(Flintstones) + nops(Kids) - nops(Flintstones intersect Kids); que, claro, que é! Como outro exemplo, considere um problema de determinar o número de inteiros positivos menor ou igual a 1000 que não são divisíveis por 2 ou 111 ao mesmo tempo. Primeiro, nós geraremos um conjunto de inteiros positivos menor ou igual a 1000. hundred := seq(i, i = 1..1000): Isto mostra como você pode usar o iterador Maple seq para gerar os membros de um conjunto. A seguir, vamos nos livrar dos elementos que são divisíveis por 2. A := hundred minus seq(2 * i, i = 1..1000): E daqueles que são divisíveis por 7: B := hundred minus seq(7 * i, i = 1..1000): (Perceba o uso combinado dos operadores seq e minus; eles trabalham bem convenientemente juntos aqui) Nós estamos procurando por inteiros que pertencem a um ou ambos de A e B, que é a união deles, então queremos o tamanho de um conjunto , o qual é nops(A union B); De acordo com o princípio de inclusão e exclusão, este valor também pode ser computado como nops(A) + nops(B) - nops(A intersect B); O mesmo princípio pode ser usado para exemplos maiores. Aqui, descrevemos o que precisa ser feito para determinar o número de inteiros positivos menor que 10.000 que são indivisíveis pelos primos 2, 3, 5 e 7. Para fazer isso, vamos usar o princípio da inclusão e exclusão para contar esses inteiros menor que 10000, que são divisíveis por, ao menos, um destes quatro números primos, e depois subtraí-los de 10000. Primeiro, criamos um conjunto de inteiros positivos menor ou igual do que um mil. th := seq(i, i=1..10^3): Agora, os inteiros menores que 10000 que são divisíveis por um dos 2, 3, 5 e 7 são os da união dos conjuntos th2 := th intersect seq(2*i, i=1..1000): th3 := th intersect seq(3*i, i=1..1000): th5 := th intersect seq(5*i, i=1..1000): th7 := th intersect seq(7*i, i=1..1000): (Note que não temos que permitir o índice i para alcançar 10000 em cada um destes, mas é mais simples deste modo, uma vez que irá descartar os valores desnecessários por tomar a interseção). A seguir, criamos conjunto de inteiros que são divisíveis por estes quatro primos em pares. th_2_3 := th intersect seq(2*3*i, i=1..1000): th_2_5 := th intersect seq(2*5*i, i=1..1000): th_2_7 := th intersect seq(2*7*i, i=1..1000): th_3_5 := th intersect seq(3*5*i, i=1..1000): th_3_7 := th intersect seq(3*7*i, i=1..1000): th_5_7 := th intersect seq(5*7*i, i=1..1000): Contamos também os inteiros menores que 10000 que são divisíveis pelos números em triplas. th_2_3_5 := th intersect seq(2*3*5*i, i=1..1000): th_2_3_7 := th intersect seq(2*3*7*i, i=1..1000): th_2_5_7 := th intersect seq(2*5*7*i, i=1..1000): th_3_5_7 := th intersect seq(3*5*7*i, i=1..1000): Finalmente, contamos os números menores que 10000 que são divisíveis por todos os quatro número 2, 3, 5 e 7. th_2_3_5_7 := th intersect seq(2*3*5*7*i, i=1..1000): Agora, para calcular os números inteiros menores que 10000 que são divisíveis por pelo menos um dos 2, 3, 5 e 7, nós calculamos como se segue. nops(th2) + nops(th3) + nops(th5) + nops(th7); % - (nops(th_2_3) + nops(th_2_5) + nops(th_2_7)); % - (nops(th_3_5) + nops(th_3_7) + nops(th_5_7)); % + (nops(th_2_3_5) + nops(th_2_3_7) + nops(th_2_5_7)); % + nops(th_3_5_7) - nops(th_2_3_5_7); Portanto, o número de inteiros menores que 10000 que não são divisíveis por 2, 3, 5 ou 7 é 1000 - %; =='''Funções geradoras'''== Funções geradoras são ferramentas poderosas para modelar conjuntos de objetos e suas construções. Por exemplo, se um conjunto de objetos é construído a partir de dois outros através da realização de um produto cartesiano de dois conjuntos subjacentes, em seguida, a função geradora para o novo conjunto é muitas vezes apenas o produto das funções geradoras pelos dois conjuntos subjacentes. Assim, saber como um conjunto é construído pode nos ajudar a construir a sua função geradora. Se você pensar nas funções geradoras como polinômios, em seguida, cada objeto do conjunto original está representado nesta expansão do produto dos dois polinômios por um monômio como <math> x ^ 5</math>. Várias combinações diferentes pode levar a um <math> x ^ 5</math>. O coeficiente de <math> x ^ 5</math> na função geradora expandido indica o número de tais objetos no novo conjunto. Os coeficientes da função geradora expandida forma uma sequência de números - o número de objetos em seu conjunto de cada tamanho. Assim, muitas vezes nos referimos a uma função geradora como a função geradora para a sequência --- seus coeficientes. Em particular, tais sequências também podem ser descritas por relações de recorrência. Aqui, vamos discutir como usar as funções geradoras para nos ajudar a resolver essas relações de recorrência. A '''função geradora''' <math> g(x)</math> para uma sequência <math> r_{n} </math> é a série de potência formal <math>\ sum_ {k = 0} ^ {\ infty} r_ {k} x ^ {k} = r_ {0} + r_ {1} x + r_ {2} x ^ {2} + r_ {3} x ^ {3} + \ cdots + r_ {n} x ^ {n} + \ cdots </math> Ele é chamado formal, porque não estamos mesmo interessados em avaliá-lo como uma função de x. Todo o nosso foco está em encontrar fórmulas para seus coeficientes. Em particular, isto significa que não há problemas de convergência a serem investigados. Maple fornece extensas habilidades para a manipulação de séries de potências formais (ou seja, as funções geradoras). Pertencem ao pacote powseries do Maple, de modo que parar acessa-las, você deve carregar este pacote. with (powseries); A primeira coisa que precisamos fazer é aprender a criar uma série de potência. Para isso, o Maple fornece a função '''powcreate'''. Ela toma como argumentos uma sequência de equações que definem o coeficiente geral. As equações especificam uma maneira de calcular o coeficiente kth em <math> \ sum_ {k = 0} ^ {\ infty} a_ {k} x ^ {k}</math>. Por exemplo, a função exponencial formal, o que tem de representação de série de potência <math>\ exp (s) = \ sum_ {n = 0} ^ {\ infty} \ frac {s ^ {n}} {n!}</math> pode ser criado em Maple emitindo a chamada powcreate (e (n) = 1 / N!); O que torna isto especialmente útil para trabalhar com relações de recorrência é que o coeficiente geral não precisa ser especificado na forma fechada (como foi acima). Você pode especificar uma relação de recorrência satisfeita com os coeficientes, em conjunto com suficientemente muitas condições iniciais para garantir uma solução única para a recorrência. Vamos ver um exemplo disso. Para criar a função geradora para a sequência de Fibonacci, a qual é definida pela relação de recorrência <math> F_ {n} = F_ {n-1} + F_ {N-2} \ hspace {3EX} \ mbox {e} \ hspace {3EX} F (0) = 1, F (1) = 1 </math> podemos entrar powcreate (f (n) = f (n - 1) + f (n - 2), F (0) = 1, F (1) = 1); Agora, a única informação interessante em uma função geradora é a sequência de seus coeficientes. Maple fornece uma maneira de acessar um coeficiente arbitrário em uma série de potências formais. Isto é feito como se segue. Para Maple, cada série de potências formais é, na verdade, um procedimento, que leva argumentos inteiros. O valor retornado por uma série de potências formais, quando dado um inteiro n como argumento é o coeficiente de <math> x ^ {n}</math> . Assim, por exemplo, o quinto número de Fibonacci pode ser produzido chamando a série de potências formais f acima com '5' como argumento. f (5); De fato, o coeficiente geral pode ser obtido fazendo passar o argumento especial '''_k''' F (_K); Para exibir uma função geradora, é melhor usar a função '''tpsform''' do Maple. Esse procedimento converte uma série de potências formal sobre uma série de potência truncada de grau especificado. Por exemplo, para exibir os dez primeiros termos da função geradora para a nossa sequência de Fibonacci, podemos usar '''tpsform''', como se segue. tpsform (F, X, 9); Funções geradoras são mais do que apenas uma forma conveniente para representar sequências numéricas e seus conjuntos de objetos associados. Eles são uma ferramenta poderosa para a solução de relações de recorrência, bem como outros tipos de problemas de contagem. Este poder deriva de nossa capacidade de manipulá-los, mais ou menos, como séries de potência comuns de Cálculo e de interpretar essas manipulações em termos de sua ação sobre os conjuntos. Assim como é feito em Cálculo com a série de potência comum, funções geradoras podem ser adicionadas, multiplicadas, multiplicadas por escalares e polinômios, compostas, avaliadas e mesmo diferenciadas e integradas. É importante reconhecer que estamos falando aqui de diferenciação formal e integração --- não há limites para se preocupar. É ainda mais importante, associar estas operações algébricas com operações combinatórias que você pode realizar no conjunto de objetos implicitamente representadas pela função geradora. Por exemplo, considerando a união de dois conjuntos disjuntos de objetos corresponde a adição de suas funções geradoras. Cada uma das operações são muitas vezes melhor pensadas em termos do seu efeito sobre os monômios que representam os objetos individuais do conjunto subjacente de objetos. Por exemplo, se um único objeto feito de de cinco sub-objetos é representado por <math> x ^ 5</math>, então existem exatamente 5 maneiras de escolher uma dessas sub-objetos para remoção. O conjunto de objetos produzidos através disso de todas as maneiras possíveis seriam representados por <math>5 x ^ 4</math>. Assim, em um sentido muito real, esta operação combinatória de dividir um único objeto desta forma corresponde à operação conhecida de diferenciação em sua função geradora. Todas as operações mais comuns que você pode realizar em séries de potência comum têm interpretações combinatórias úteis e podem ser realizadas em nossas séries de potência formal. Em cada caso, pode especificar o que tal efeito terá sobre o coeficiente da série. Maple fornece habilidades para a realização de todas estas manipulações, e muito mais. Estas habilidades são melhor demonstradas pelo trabalhar através de um exemplo. Usaremos Maple para resolver a recorrência Fibonacci com funções geradoras. Se multiplicarmos ambos os lados da recorrência Fibonacci <math> F_ {n} = F_ {n-1} + F_ {N-2} </math> por <math>x ^ {n}</math>, obtemos <math>F_ {n} x ^ {n} = f_ {n-1} x ^ {n} + f_ {n-2} x ^ {n}</math> Agora soma de n = 1 rende <math>\ sum_ {n = 1} ^ {\ infty} f_ {n} x ^ {n} = \ sum_ {n = 1} ^ {\ infty} f_ {n-1} x ^ {n} + \ sum_ {n = 1} ^ {\ infty} {f_ n-2} x ^ {n}</math> O lado esquerdo desta equação difere da função geradora apenas o primeiro termo (em que n = 0), e as somas no lado direito podem ser fatoradas, assim que obtemos <math>g (x) - 1 = XG (x) + x ^ {2} g (x)</math> Agora, resolver esta equação para g (x) produz <math>g (x) = \ frac {-1} {x ^ {2} + x - 1}</math> =='''Cálculos e como explorá-los'''== Esta seção apresentará algumas soluções em Maple para alguns dos problemas. Nós nem sempre deve apresentado aqui uma solução completa; em alguns casos, nós apenas sugerimos uma ou duas coisas para você experimentar, e deixar a implementação detalhado para você. O próximo problema que deve ser considerado é que a determinação do número menor de Fibonacci que ultrapassa um milhão, um bilhão e um trilhão. '''Solução''' Podemos resolver isso muito facilmente dentro do Maple, usando um loop while simples. Antes, porém, precisamos ter certeza de que temos a função fibonacci correta. with(combinat); Isso define a versão correta da função Mapple fibonacci para nós. Há uma outra função, também chamada de '''Fibonacci''' no pacote '''linalg''', mas é a função errada. A idéia aqui é de varrer o índice para a seqüência de Fibonacci até que o valor da seqüência atingir um limite especificado (digamos, um milhão). A construção de loop while em Maple é ideal para este tipo de aplicação. count := 1; # inicializa o contador while fibonacci(count) <= 1000000 do count := count + 1; od: print(fibonacci(count)); Podemos ver que o número de Fibonacci nos dá esse valor, verificando o valor da variável '''count'''. count; É, provavelmente, também uma boa idéia para verificar a nossa lógica, e ver que o número de Fibonacci anterior é realmente inferior a 10 milhões. fibonacci(count - 1); Agora, devemos verificar isso por mais alguns valores ainda maiores do que um milhão. No entanto, uma vez que você já tentou duas ou três, você certamente vai querer experimentar mais, por isso é provavelmente uma boa idéia quebrar este pequeno loop while dentro de uma função (que vamos chamar BigFib). BigFib := proc(n) calcula o menor número Fibonacci com excedente n local k; with(combinat); k := 1; while fibonacci(k) <= n do k := k + 1; od; print(fibonacci(k)); end: Para fazer a nossa função corretamente, chamamos '''with(combinat)''' no corpo da função para garantir que temos a versão correta da função de Fibonacci. (Isso também poderia ser alcançado usando a sintaxe de chamada longa '''combinat[fibonacci]''' para a função.) Agora é bastante simples para calcular o número de Fibonacci menor superior a um determinado número. BigFib(1000000000); BigFib(1000000000000); BigFib(10^10); ==='''2. Encontrar tantas números de Fibonacci primos que puder.'''==== '''Solução''' Usando Maple, este tipo de problema torna-se muito simples; Nós pode simplesmente usar o procedimento de Fibonacci do Maple, do pacote '''combinat''' para gerar números de Fibonacci, e podemos usar a função '''ISPrime''' para testar a primalidade de cada um. Apesar de ser muito simples, vamos finalizar em um procedimento, para que possamos chamá-lo com argumentos diferentes que determinam quantos números de Fibonacci serão testado. PrimeFib := proc(n) local i, # loop index t, # temporary variable prime_fib; # list of prime Fibonacci numbers; returned prime_fib := NULL; for i from 1 to n do t := combinat[fibonacci](i); if isprime(t) then prime_fib := prime_fib, t; fi; od; RETURN(prime_fib); end: Aqui, para economizar espaço, testamos apenas os primeiros 1000 números Fibonacci. PrimeFib(100); Note-se que, uma vez que usamos '''ISPrime''', nossa lista não é certa de ser composta somente de números primos, como ISPrime usa um teste de primaridade probabilística. Outra abordagem que você pode considerar tentar é construir duas listas: uma contendo a lista de números de Fibonacci até certo ponto, e outro contendo a sequência de números primos, gerados utilizando a função ithprime (que não é probabilística). Em seguida, cruzar as duas listas para extrair todos os membros que têm em comum. Esta abordagem tem a vantagem de que evita a utilização do teste de primaridade probabilística utilizado por ISPrime. ==='''5. Encontre todos os números primos que não excedam 10000, usando o crivo de Eratóstenes.'''=== '''Solução'' Implementar o crivo de Eratóstenes é um exercício não-trivial em qualquer linguagem de programação, mas Maple torna isso mais fácil do que a maioria. O crivo produz uma lista de todos os números primos que não excedam um dado número inteiro positivo n. Vamos modelar a lista de números inteiros de 1 a n por um booleano com valores em um vetor obtido com o ISPrime. A i-ésima entrada do ISPrime terá o valor verdadeiro se i é um número primo, e false de outra forma. No início do algoritmo, todas as entradas são inicializados como false. Isto corresponde a ter escrito a lista de números de 1 a n, mas não ter tirado nenhum para fora. Para tirar um número, definimos seu valor o vetor ISPrime para false. Progredindo através do algoritmo detecta a não-primaridade, e entradas serão marcadas como falsas à medida que são descobertos a ser múltiplos. Nosso programa consiste principalmente de três para loops. O primeiro simplesmente inicializa o vetor ISPrime, enquanto o terceiro loop for imprime os resultados. O crivo em si é o meio para o laço. Usamos três novas funções no código. A função do vetor simplesmente cria um vetor não inicializado. A função '''isqrt''' produz uma aproximação inteira da raiz quadrada do seu argumento. O novo recurso mais interessante é a chamada pdo tipo, que testa se seu primeiro argumento tem o tipo de seu segundo argumento. Aqui, está sendo utilizado para testar se o resultado da divisão é um número inteiro, o que determina se eficazmente um número inteiro divide outro. Outra maneira de fazer isso seria usar a função de '''irem''', que resulta no resto depois de dividir seu primeiro argumento pelo seu segundo argumento. irem(5,2); irem(6,2); A linha que lê se o tipo (j/i, integer) então, no nosso código, poderia ser substituida por if irem(j,i) = 0 então. Aqui está o código Esieve := proc(n) local i,j, # loop indices isPrime, # array of booleans prime_list, # list of primes sqrtn; # integer approx. of sqrt(n) opções traçadas inicialiar o vetor isPrime := table(); isPrime[1] := false; for i from 2 to n do isPrime[i] := true od; obter uma aproximação de inteiro para a raiz quadrada do argumento 'n'(adicionar 1 por segurança). sqrtn := 1 + isqrt(n); o crivo verdadeiro for i from 1 to sqrtn do pular isso se não for primo if isPrime[i] then for j from i+1 to n do testa se i divide if type(j/i, integer), então if irem(j,i) = 0 then isPrime[j] := false fi; od; fi; od; converte a lista de booleanos para uma lista de primos prime_list := NULL; for i from 1 to n do if isPrime[i] then prime_list := prime_list, i; fi; od; RETURN(prime_list); end: Agora tente! Esieve(10); Esieve(100); Esieve(1000); =='''Exemplos Extras'''== '''Exemplo 1 (página 415)''' Resolva: <math>a_n = 2a_{n-1} + 3a_{n-2}, a_0 = 0, a_1 = 1 </math> ''Solução'': Usando <math> a_n = r^n </math>, a equação característica a seguir é obtida: <math> r^2 - 2r - 3 = 0 </math> Os fatores do lado esquerdo como <math>(r-3)(r+1)</math>, obtendo-se as raízes 3 e -1. Assim, a solução geral para a relação de recorrência dada é <math>a_n = c . 3^n + d(-1)^n</math>. Usando as condições iniciais <math>a_0 = 0</math> e <math>a_1 = 1 </math> constrói-se um sistema de equações <math> c . 3^0 + d (-1)^0 = 0</math> <math> c . 3^1 + d (-1)^1 = 1</math> ou <math> c + d = 0</math> <math> 3c - d = 1</math> Com solução de <math> c = \frac{1}{4}</math> e <math> d = -\frac{1}{4}</math>. Dessa forma, a solução para a a relação de recorrência dada é <math> a_n = \frac{1}{4}.3^n - \frac{1}{4}.(-1)^n</math> Nota: Poderíamos ter invertido a ordem das raízes quando escrevemos a solução geral: <math>a_{n} = c . (-1)^n + d . 3^n = 0</math> Se fizermos isso, a partir das condições iniciais obtemos <math> c + d = 0</math> <math> -c + 3d = 1</math> Com soluções <math> c = -\frac{1}{4}</math> e <math> d = \frac{1}{4}</math>. A solução para relação de recorrência dada é <math> a_n = - \frac{1}{4}.(-1)^n + \frac{1}{4}.3^n </math> a qual é a mesma que obtivemos anteriormente. A ordem em que posicionamos as raízes não importa. '''Exemplo 2 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 + 7r + 10 = 0 </math> , ou <math>{(r + 5) (r + 2)}</math> . As raízes são -5 e -2; assim a solução geral é <math> a_n = c . {(-5)^n} + d . {(-2)^n} </math> As condições iniciais constroem o sistema de equação <math> c + d = 3</math> <math> -5c - 2d = 3</math> A solução para o Sistema é c=-3 e d=6. Assim, a solução para a relação de recorrência é <math> a_n = {(-3)} {(-5)^n} + 6{(-2)^n} </math> '''Exemplo 3 (página 415)''' Resolva: <math>a_n = - 7a_{n-1} - 10a_{n-2}, a_0 = 3, a_1 = 3 </math> ''Solução'': Usando <math> a_n = r^n </math> obtem-se a equação característica <math> r^2 - 10r + 25 = 0 </math> , ou <math>{(r - 5) (r - 5)}</math> , com 5 como uma solução repetida. Dessa forma, a solução geral é <math> a_n = c . 5^n + d . n . 5^n </math> As condições iniciais constroem o sistema de equação <math> c . 5^0 + d . 0 . 5^0 = 3</math> <math> c . 5^1 + d . 0 . 5^1 = 4</math> ou <math> c = 3</math> <math> 5c + 5d = 5</math> A solução para o Sistema é <math>c= 3</math> e <math>d = -\frac{11}{5}</math>. Assim, a solução para a relação de recorrência é <math>a_n = 3 . 5^n - \frac{11}{5} . n . 5^n</math> '''Exemplo 4 (página 415)''' Resolva: <math>a_n = 3a_{n-1} + 1, a_0 = 4</math>, por substituição para <math>a_{n-1}</math>, depois <math> a_{n-2} </math>, etc. ''Solução'': Começando com <math>a_n = 3a_{n-1} + 1</math> e substituindo <math> a_{n-1} </math> por <math> a_{n-2} </math> , depois por <math> a_{n-3} </math>, etc., obtem-se: <math>a_n = 3a_{n-1} + 1</math> <math>a_n = 3{(a_{n-2} + 1)}+1</math> <math>a_n = 3^2a_{n-2} +3 . 1+1</math> <math>a_n = 3^2{(a_{n-3} + 1)} +3 . 1+1</math> <math>a_n = 3^3a_{n-3} + 3^2 . 1 + 3 . 1 + 1</math> <math>a_n = 3^na_0 + {(3^n-1 + 3^n-2 + ... + 3^2 + 3 + 1)}</math> <math>a_n = 4 . 3^n + \frac{3^n-1}{2} </math> <math>a_n = \frac{8 . 3^n}{2} + \frac{3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{9 . 3^n}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^{23^n}}{2} - \frac{1}{2} </math> <math>a_n = \frac{3 ^n+2}{2} - \frac{1}{2} </math> '''Exemplo 5 (página 415)''' Suponha que a equação característica de uma relação de recorrência linear homogênea com coeficientes constantes é <math>{(r - 3)^4}{(r - 2)^3}{(r+6)} = 0 </math> Escreva a solução geral da relação de recorrência. ''Solução'': As raízes são 3, 2, e -6, com multiplicidades 4, 3, e 1, respectivamente. Consequentemente, a solução geral é: <math>a_n = {(a3^n + bn3^n + cn^{23^n} + dn^{33^n})} + h{(-6)^n}</math> <math>a_n = a3^n + bn3^n + cn^{23^n} + dn^{33^n} + e2^n + fn2^n + gn^{22^n} + h{(-6)^n}</math> '''Exemplo 6 (página 415)''' Resolva a relação de recorrência <math>a_n = 3a_{n-1} + 2^n</math>, com condição inicial <math> a_0 = 2 </math>. ''Solução'': A relação de recorrência homogênea associada é <math>a_n = 3a_{n-1}</math>. Esta equação característica é <math>r - 3 = 0</math>, em que tem solução <math>r = 3</math>. Portanto, a solução geral associada a relação de recorrência homogênea é <math>a_n = a3^n</math> . Para obter uma solução específica para a relação de recorrência dada, tente <math> a_{n}^{(p)} = c2^n</math> , obtendo <math>c2^n = 3c2^{n-1} + 2^n</math>, em que produz <math>c = -2</math>. Portanto a solução específica é <math> a_{n}^{(p)} = -2^{n+1}</math>. Consequentemente, a solução geral para a relação de recorrência dada é <math>a_n = a3^{n} - 2^{n+1}</math> A condição inicial <math>a_0 = 2</math> dá <math>2 = a3^{0} - 2^{0+1}</math>, ou <math>2 = a - 2</math>, com solução <math>a = 4</math>. Assim, a solução para a relação de recorrência heterogênea dada é <math>a_n = 4 . 3^{n} - 2^{n+1}</math>. '''Exemplo 7 (página 415)''' Resolva a relação de recorrência <math>a_n = 8a_{n-1} - 12a_{n-2} + 3n</math>, com as condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math>. ''Solução'': A equação característica para a relação de recorrência homogênea associada é <math>r^2 -8r +12 = 0</math>, em que tem soluções <math>r=6</math> e <math>r=2</math>. Deste modo, a solução geral para a relação de recorrência homogênea é <math>a_n = a . 6^n + b . 2^n</math>. Para obter uma solução específica para a relação de recorrência dada, tente <math>p_n = cn + d</math>, obtendo <math>cn + d = 8[c(n-1)+d] - 12[c(n-2)+d] + 3n</math> Em que pode ser reescrito como <math> n(c – 8c + 12c - 3) + (d + 8c – 8d - 24c + 12d) = 0</math> Uma vez que o coeficiente do termo n e o termo constante devem ser cada um igual a 0, temos as duas equações <math> c – 8c + 12c - 3 = 0</math> <math> d + 8c – 8d – 24c + 12d = 0</math> ou <math>5c – 3d = 0</math> <math>-16c + 5d = 0</math> Resolvendo c e d, temos c=3/5 e d=48/255 e a solução específica <math>p_n = \frac{3}{5}n + \frac{48}{25}</math> Portanto, <math>a_n = a6^n + b2^n + p_n = a6^n + b2^n + \frac{3}{5}n + \frac{48}{25}</math> Usando as duas condições iniciais <math>a_0 = 1</math> e <math>a_1 = 5</math> obtem-se o sistema de equações <math>a6^0 + b2^0 + \frac{3}{5}0 + \frac{48}{25} = 1</math> <math>a6^1 + b2^1 + \frac{3}{5}1 + \frac{48}{25} = 5</math> E a solução é encontrada, sendo <math>a = \frac{27}{255}</math> e <math>b = -2</math>. Dessa forma, a solução para a relação de recorrência dada é <math>a_n = \frac{27}{25}6^n + 2^{n+1} + \frac{3}{5}n + \frac{48}{25}</math> 01ef614f9670d769f2630e59e10c2564cff5a521 Fundamentos Matemáticos da Computação 1 0 80 504 170 2016-05-26T12:29:27Z Clah 19 wikitext text/x-wiki [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]] [[Somatório e Produtório]] [[Álgebra Booleana]] 196ec67d0fcdb6e625cc0c4481e81f439604d1ea 508 504 2016-05-26T12:47:30Z Clah 19 wikitext text/x-wiki [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]] [[Somatório e Produtório]] 32f2eb03d33d16e0d50bddf238287bc7174d461f Álgebra Booleana 0 124 505 2016-05-26T12:29:54Z Clah 19 Created page with "Álgebra Booleana Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser ..." wikitext text/x-wiki Álgebra Booleana Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). 00bcc21cad13bbf4b432fdf23149eabe78dd62d7 506 505 2016-05-26T12:45:35Z Clah 19 wikitext text/x-wiki Álgebra Booleana Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == 1. Funções Booleanas == Em Maple, há um construtor do tipo booleano, isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e em Maple eles são representados por literais ‘true’ e ‘false’. Para o Maple, estas duas são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. '''''a := true;'''''<\b> '''''b := false;''''' Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, Maple oferece dois conjuntos de operadores booleanos para realizar operaçoes em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘and’, ‘or’ e ‘not’. Em ceb0b9e92a7306cbeee79ca5b9c8125a78e42a0b 510 506 2016-05-26T12:54:47Z Clah 19 wikitext text/x-wiki .Álgebra Booleana Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em Maple, há um construtor do tipo booleano, isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e em Maple eles são representados por literais ‘true’ e ‘false’. Para o Maple, estas duas são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, Maple oferece dois conjuntos de operadores booleanos para realizar operaçoes em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘and’, ‘or’ e ‘not’. Em 42ffdca9b7869d1067c57c8e8fc3b9459665ccc4 511 510 2016-05-26T12:57:30Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo booleano, isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘true’ e ‘false’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, Maple oferece dois conjuntos de operadores booleanos para realizar operaçoes em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘and’, ‘or’ e ‘not’. Em ''Maple'' também é disponibilizado os operadores &and, &or e &not para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote lógico da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: 4165231bc054ee8ffe616f93afdc7c0ca1cc9f9f 512 511 2016-05-26T13:02:57Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo '''boolean''', isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘'''true'''’ e ‘'''false'''’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;<\pre> f3c7808375752286e9d38dc08811840a6b75db01 Fundamentos Matemáticos da Computação 2 0 125 509 2016-05-26T12:47:46Z Clah 19 Created page with " [[Álgebra Booleana]]" wikitext text/x-wiki [[Álgebra Booleana]] 6f2e8d42497f7bcb90299bdd732241d23f86a337 Álgebra Booleana 0 124 513 512 2016-05-26T13:16:20Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo '''boolean''', isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘'''true'''’ e ‘'''false'''’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> b6c95f1cb6acea77b6d86d514cf3bc4e655d5214 514 513 2016-05-26T13:18:52Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo '''boolean''', isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘'''true'''’ e ‘'''false'''’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> <pre>a := 'a';</pre> <pre>not not a;</pre> 130d8c400c34f1e2ba4f6d290180bf92d1da2062 515 514 2016-05-26T13:19:51Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo '''boolean''', isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘'''true'''’ e ‘'''false'''’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> <pre>a := 'a';</pre> <pre>not not a;</pre> <pre>&not &not a;</pre> <pre>&not (&not a);</pre> <pre>a and b and c and d;</pre> <pre>true and true; true and false; false and true; false and false;</pre> e9d32350c740b9b8de21f373d2bf6368b1bcfd07 516 515 2016-05-26T13:20:15Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo '''boolean''', isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘'''true'''’ e ‘'''false'''’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> <pre>a := 'a';</pre> <pre>not not a;</pre> <pre>&not &not a;</pre> <pre>&not (&not a);</pre> <pre>a and b and c and d;</pre> <pre>true and true; true and false; false and true; false and false;</pre> 087a66a88ce98287878f178b44959f7a0a6df380 517 516 2016-05-26T13:20:18Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == Em ''Maple'', há um construtor do tipo '''boolean''', isto é, um tipo de variável que pode ser usado para representar valores booleanos. Existem apenas dois valores booleanos e em ''Maple'' eles são representados por literais ‘'''true'''’ e ‘'''false'''’. Para o ''Maple'', estes dois são valores constantes, assim como o numero 2 ou a matrix identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de ‘a’) <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênnteses. O operador booleano ‘'''and'''’ é um operador binário, ele modela a semântica lógica ''and''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possiveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos ‘'''and'''’ para dois valores booleanos é '''true''', precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que '''and''' é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador ‘'''or'''’ é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador not, é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que not é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes! Os operadores booleanos em Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binãrio significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões A mesma coisa é verdade para o operador '''or'''. Outra propriedade dos quais você deve estar ciente é que não é uma involução: <pre>a and b and c and d;</pre> Em outras palavras, a segunda aplicação de '''not''' desfaz o efeito da primeira. Vocẽ pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas om o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto é porque operadores booleanos inertes são usados primeiramente para trabalhar simbolicamente com operações booleanoas; eles são operadores sobre os quais as ferramentas do pacote '''logic''' de Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão: <pre>&not( &not ( a ) );</pre> Não é tão simplificado em '''a'''. 6b74eb129937108ac700c661080b8f55fd6de43f 518 517 2016-05-26T15:26:53Z Clah 19 /* 1. Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo ““booleano””, isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais ““true”” e ““false””. Para o Maple, estes são dois valores constantes, assim como o numero ““2”” ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variavel então o valor da variavel será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de ‘a’) <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênnteses. O operador booleano ‘'''and'''’ é um operador binário, ele modela a semântica lógica ''and''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possiveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos ‘'''and'''’ para dois valores booleanos é '''true''', precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que '''and''' é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador ‘'''or'''’ é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador not, é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que not é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes! Os operadores booleanos em Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binãrio significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões A mesma coisa é verdade para o operador '''or'''. Outra propriedade dos quais você deve estar ciente é que não é uma involução: <pre>a and b and c and d;</pre> Em outras palavras, a segunda aplicação de '''not''' desfaz o efeito da primeira. Vocẽ pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas om o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto é porque operadores booleanos inertes são usados primeiramente para trabalhar simbolicamente com operações booleanoas; eles são operadores sobre os quais as ferramentas do pacote '''logic''' de Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão: <pre>&not( &not ( a ) );</pre> Não é tão simplificado em '''a'''. ffb92a44e62405a3f300759249eced3a153c20c8 519 518 2016-05-26T15:28:25Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de ‘a’) <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênnteses. O operador booleano ‘'''and'''’ é um operador binário, ele modela a semântica lógica ''and''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possiveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos ‘'''and'''’ para dois valores booleanos é '''true''', precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que '''and''' é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador ‘'''or'''’ é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador not, é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que not é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes! Os operadores booleanos em Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binãrio significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões A mesma coisa é verdade para o operador '''or'''. Outra propriedade dos quais você deve estar ciente é que não é uma involução: <pre>a and b and c and d;</pre> Em outras palavras, a segunda aplicação de '''not''' desfaz o efeito da primeira. Vocẽ pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas om o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto é porque operadores booleanos inertes são usados primeiramente para trabalhar simbolicamente com operações booleanoas; eles são operadores sobre os quais as ferramentas do pacote '''logic''' de Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão: <pre>&not( &not ( a ) );</pre> Não é tão simplificado em '''a'''. abd09893fd1b178f333669fb3e011994b8d8e381 520 519 2016-05-26T15:32:12Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênnteses. O operador booleano ‘'''and'''’ é um operador binário, ele modela a semântica lógica ''and''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possiveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos ‘'''and'''’ para dois valores booleanos é '''true''', precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que '''and''' é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador ‘'''or'''’ é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador not, é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que not é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes! Os operadores booleanos em Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binãrio significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões A mesma coisa é verdade para o operador '''or'''. Outra propriedade dos quais você deve estar ciente é que não é uma involução: <pre>a and b and c and d;</pre> Em outras palavras, a segunda aplicação de '''not''' desfaz o efeito da primeira. Vocẽ pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas om o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto é porque operadores booleanos inertes são usados primeiramente para trabalhar simbolicamente com operações booleanoas; eles são operadores sobre os quais as ferramentas do pacote '''logic''' de Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão: <pre>&not( &not ( a ) );</pre> Não é tão simplificado em '''a'''. 318225e798f7d592f1292c19a6fd9884e66f56a9 521 520 2016-05-26T15:34:44Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variaveis com simplicidade. Eles estão disponiveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênnteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possiveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operdor '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>&not( &not ( a ) );</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar “simbolicamente” com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote lógico do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão &not( &not ( a ) ); Não é tão simplificado em '''a'''. 1c7cf6779a743671ca0c807fa1b3a8646926969c 522 521 2016-05-26T15:37:41Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>&not( &not ( a ) );</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar “simbolicamente” com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote lógico do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é tão simplificado em '''a'''. 093cabd09d40140a0c5fd2ff1fe1a2ab5d916bf6 523 522 2016-05-26T15:41:15Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar “simbolicamente” com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote lógico do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é tão simplificado em '''a'''. 133f13500474f30f57be0d41086de82fef35d15e 524 523 2016-05-26T15:45:40Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ‘’simbolicamente’’ com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. af7bdefb7bcde8c7964d11f4fff80545f8f8116a 525 524 2016-05-26T15:46:53Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possiveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nos veremos que a expresão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. ff3b3c61beecc3231e42c0e398d2dca9e7fc3256 526 525 2016-05-26T15:47:54Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores ‘'''and'''’, ‘'''or'''’ e ‘'''not'''’. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. 2f948e2eebfc1764d8c8e2293886204d2d27da4b 527 526 2016-05-26T15:50:37Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador ‘'''and'''’ produz seu valor imediato, enquanto que o segundo ‘&and’ é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador ‘'''&not'''’. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. affdfa419e1797a42dd88464c928ae403a251e23 528 527 2016-05-26T15:51:57Z Clah 19 /* Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. beb1f334bb29595707afa24848e541c4217eef13 529 528 2016-05-26T15:59:57Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''1.Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. == '''1.1. Um Avaliador Booleano''' == b73070fa9e0804298d95a40671c426acf384d129 530 529 2016-05-26T16:00:36Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''1.Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''1.1. Um Avaliador Booleano''' === 189d4c275dccf3c15e0315ba9968a623ed453efe 531 530 2016-05-26T16:17:24Z Clah 19 /* 1.1. Um Avaliador Booleano */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''1.Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''1.1. Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. ee36f452e348be24eddbe9d0daeb11043bda752a 532 531 2016-05-26T17:47:19Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''1.Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''1.1. Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''1.2. Representando Funções Booleanas''' === === '''1.3. Verificando Identidades Booleanas''' === === '''1.4. Dual''' === === '''1.5. Disjunctive Normal Form''' === =='''2. Representação de Funções Booleanas'''== be4373306bf4219a0b2eb84fcaf0dd8a8ac15c07 533 532 2016-05-26T17:50:57Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === === '''Verificando Identidades Booleanas''' === === '''Dual''' === === '''Disjunctive Normal Form''' === == '''Representação de Funções Booleanas''' == 3228ada4485ecc4835c7ed0c3614bf56233496a2 534 533 2016-05-26T18:00:10Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a sum é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como, então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajuda na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === === '''Dual''' === === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == fffd073c4e5277ab9ba3ca91ea3dcb51d979cd18 535 534 2016-05-26T18:02:18Z Clah 19 /* Representando Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a sum é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como, então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajuda na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === === '''Dual''' === === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 7ff8512916c1ebc824c42e40a20234d0644ff05a 536 535 2016-05-26T18:04:51Z Clah 19 /* Representando Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === === '''Dual''' === === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 219157cd207868143475042c84080031bfd18eb7 537 536 2016-05-27T00:58:05Z Clah 19 /* Verificando Identidades Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == bc22d0e5c50c4c481f28a211c9e7c786c0959d38 538 537 2016-05-27T01:41:24Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == f86659bd577d5a94f35644f822f5fa66afc37582 540 538 2016-05-27T01:47:18Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === [[File:imagem.png|400px]] === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == a96833f4953dc75602afc55ea4b55c555ff932bb 541 540 2016-05-27T01:48:13Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === [[File:imagem.png|200px]] === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == f20e8e45c70811535b11db89f1828865d86853ac 542 541 2016-05-27T01:49:00Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta -- isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == e56c02fda925d9d1e1c9308a56d5c3fc9881e1c5 543 542 2016-05-27T01:51:04Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta -- isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == ae41bc23be477cf62c9b7b90e52cc96f5058cef5 544 543 2016-05-27T01:52:00Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta -- isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 48aa4c53b17dfbc1f139b524eb474dbc845d6bbe 545 544 2016-05-27T01:53:28Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 1535bcb936fe2d207b16bc10b8be42587e230886 546 545 2016-05-27T01:57:00Z Clah 19 /* Forma Normal Disjuntiva */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 4cffb42202381f494ac39af83d07c9ecc7902cb7 547 546 2016-05-27T02:31:24Z Clah 19 /* Representação de Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 10aef96216cf36eee2e7eab8d1aeecbccd11a47d 548 547 2016-05-27T02:34:46Z Clah 19 /* Minimização de Expressões Booleanas e Circuitos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == <math>2_{2_{n}}</math> == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 70d2832e52c3acf00f167a1409dc2b5b90d72f26 549 548 2016-05-27T02:35:41Z Clah 19 /* Minimização de Expressões Booleanas e Circuitos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == <math>{2_{2_{n}}}</math> == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == e82ae9181fce3463dc4bc740ca03460c996952b3 550 549 2016-05-27T02:36:21Z Clah 19 /* Minimização de Expressões Booleanas e Circuitos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == <math>2^2^n</math> == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 33013e8780c3e206534efbb6f0c79af903a40b2f 551 550 2016-05-27T02:36:59Z Clah 19 /* Minimização de Expressões Booleanas e Circuitos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == <math>2^{2^{n}}</math> == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 183a128d5027889a9ae4e55409853ef60d009434 552 551 2016-05-27T02:50:12Z Clah 19 /* Minimização de Expressões Booleanas e Circuitos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 4380eec5ce33ba750c691572c44e5075e5bc768b 553 552 2016-05-27T02:59:04Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == <math>{0, 1}^{n}</math> == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 9f0caad14dd63c34856f4d3d287e991fc523ec75 554 553 2016-05-27T02:59:16Z Clah 19 /* Cálculos e Explorações */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == <math>{0, 1}^{n}</math> == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == b59dc20e479fab2e0e27c890f84e61826c744db0 555 554 2016-05-27T02:59:36Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == <math>{{0, 1}}^{n}</math> == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == d3724aa17412de1bd9f5dad5c33c5cf70ea8b7ff 556 555 2016-05-27T03:00:28Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == <math>'{'{0, 1}'}'^{n}</math> == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 7936a2e139c3fb1353294008e046ed53805eabb3 557 556 2016-05-27T03:00:57Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == {0, 1}^n == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == f1310f2eccd8fddbfe7f947b2ffad22df28558c3 558 557 2016-05-27T03:06:52Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x,y,z)'', de três variáveis, com a seguinte tabela de verdade. x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1 Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 88772556b7b9d7fdbed9070bf7375e496a789118 559 558 2016-05-27T03:17:30Z Clah 19 /* Representação de Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f(x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == a004ddc6b68cdd855d54b38c33437d5598f4c5e7 560 559 2016-05-27T03:17:47Z Clah 19 /* Representação de Funções Booleanas */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == db0c978487adcc0d5bb0ef11944b047dc9e47370 561 560 2016-05-27T03:20:55Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta ______ Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre> x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == bed93e7a5776b8269a1a1d2cf4999a089c97216b File:Imagem.png 6 126 539 2016-05-27T01:47:05Z Clah 19 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 562 539 2016-05-27T03:21:27Z Clah 19 Clah uploaded a new version of &quot;[[File:Imagem.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Álgebra Booleana 0 124 563 561 2016-05-27T03:28:44Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|200px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 8e97fcc9995ad040577dfd8284819356cd2a2337 564 563 2016-05-27T03:29:22Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == c60c675136ac404d5e71a0d223570d30cd96eeb0 565 564 2016-05-27T03:31:09Z Clah 19 /* Condições Indiferentes */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == == '''Exercícios e Projetos''' == 7b4038f66af751337170c5f7a55cb1c317c8f92f 566 565 2016-05-27T03:34:31Z Clah 19 /* Cálculos e Explorações */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == <math>2^({2^{3}})</math> == '''Exercícios e Projetos''' == 3a23d461f542cdfdb321356079a5206d49e2f1aa 567 566 2016-05-27T03:35:12Z Clah 19 /* Cálculos e Explorações */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == <math>2^{2^{3}}</math> == '''Exercícios e Projetos''' == c2f1cd772f6b70ec85138eb10d20e9f39dc5eb6e 568 567 2016-05-27T03:40:10Z Clah 19 /* Cálculos e Explorações */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == <math>2^{2^{3}}</math> Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles. Construa a tabela da função booleana de grau 3. '''Solução''' Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas. Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso. Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a <pre>Dom3 := [false,false,false], [false,false,true], [false,true,false], [true,false,false], [true,false,true], [true,true,false], [false,true,true], [true,true,true];</pre> Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''. <pre>with(combinat):</pre> Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada <pre>writeto(terminal);</pre> Use a facilidade help do Maple para saber mais sobre essas funções, se necessário. Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los. '''Solução''' Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos. Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado. <pre>with(logic):</pre> Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar <pre>randbool(a,b);</pre> ou <pre>randbool([a,b]);</pre> Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''': <pre>for i from 1 to 10 do randbool(a,b); od;</pre> Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes. <pre>randbool(x,y,z, CNF); randbool(u,v, MOD2);</pre> Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema. A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores. O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada <pre>readlib(trace);</pre> Agora, se gerarmos um expressão aleatória booleana com quatro variáveis <pre>e := randbool(a,b,c,d);</pre> poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo <pre>bsimp(e);</pre> O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui. O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada. Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''': <pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre> Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1. <pre>Printlevel;</pre> Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle. <pre>for i from 1 to 5 do sqrt(i); od;</pre> Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple. Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto. <pre>for i from 1 to 5 do sqrt(sin(abs(i))); od;</pre> Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000. Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados. <pre>with(logic): # esteja certo que 'bsimp' está definida e := x &and y &or &not z; bsimp(e);</pre> Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte <pre>interface(verboseproc = 2); eval(bsimp); # assuma 'bsimp' carregada</pre> Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui). Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas. Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis​​, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média. == '''Exercícios e Projetos''' == e72d6b5f374188433e32d9db13e3bb1b221ad665 569 568 2016-05-27T03:41:24Z Clah 19 /* Cálculos e Explorações */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles. Construa a tabela da função booleana de grau 3. '''Solução''' Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas. Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso. Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a <pre>Dom3 := [false,false,false], [false,false,true], [false,true,false], [true,false,false], [true,false,true], [true,true,false], [false,true,true], [true,true,true];</pre> Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''. <pre>with(combinat):</pre> Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada <pre>writeto(terminal);</pre> Use a facilidade help do Maple para saber mais sobre essas funções, se necessário. Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los. '''Solução''' Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos. Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado. <pre>with(logic):</pre> Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar <pre>randbool(a,b);</pre> ou <pre>randbool([a,b]);</pre> Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''': <pre>for i from 1 to 10 do randbool(a,b); od;</pre> Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes. <pre>randbool(x,y,z, CNF); randbool(u,v, MOD2);</pre> Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema. A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores. O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada <pre>readlib(trace);</pre> Agora, se gerarmos um expressão aleatória booleana com quatro variáveis <pre>e := randbool(a,b,c,d);</pre> poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo <pre>bsimp(e);</pre> O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui. O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada. Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''': <pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre> Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1. <pre>Printlevel;</pre> Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle. <pre>for i from 1 to 5 do sqrt(i); od;</pre> Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple. Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto. <pre>for i from 1 to 5 do sqrt(sin(abs(i))); od;</pre> Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000. Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados. <pre>with(logic): # esteja certo que 'bsimp' está definida e := x &and y &or &not z; bsimp(e);</pre> Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte <pre>interface(verboseproc = 2); eval(bsimp); # assuma 'bsimp' carregada</pre> Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui). Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas. Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis​​, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média. == '''Exercícios e Projetos''' == 93bd471b6b8ed3a7d7daa169e1a123c3fea48d45 571 569 2016-05-27T03:46:45Z Clah 19 /* Dual */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem2.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles. Construa a tabela da função booleana de grau 3. '''Solução''' Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas. Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso. Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a <pre>Dom3 := [false,false,false], [false,false,true], [false,true,false], [true,false,false], [true,false,true], [true,true,false], [false,true,true], [true,true,true];</pre> Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''. <pre>with(combinat):</pre> Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada <pre>writeto(terminal);</pre> Use a facilidade help do Maple para saber mais sobre essas funções, se necessário. Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los. '''Solução''' Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos. Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado. <pre>with(logic):</pre> Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar <pre>randbool(a,b);</pre> ou <pre>randbool([a,b]);</pre> Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''': <pre>for i from 1 to 10 do randbool(a,b); od;</pre> Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes. <pre>randbool(x,y,z, CNF); randbool(u,v, MOD2);</pre> Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema. A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores. O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada <pre>readlib(trace);</pre> Agora, se gerarmos um expressão aleatória booleana com quatro variáveis <pre>e := randbool(a,b,c,d);</pre> poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo <pre>bsimp(e);</pre> O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui. O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada. Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''': <pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre> Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1. <pre>Printlevel;</pre> Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle. <pre>for i from 1 to 5 do sqrt(i); od;</pre> Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple. Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto. <pre>for i from 1 to 5 do sqrt(sin(abs(i))); od;</pre> Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000. Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados. <pre>with(logic): # esteja certo que 'bsimp' está definida e := x &and y &or &not z; bsimp(e);</pre> Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte <pre>interface(verboseproc = 2); eval(bsimp); # assuma 'bsimp' carregada</pre> Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui). Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas. Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis​​, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média. == '''Exercícios e Projetos''' == 7342c9e61088c4a802fc2aa7c786fc65c8ef2a4b 572 571 2016-05-28T18:48:54Z Paulohq 21 /* Exercícios e Projetos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem2.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles. Construa a tabela da função booleana de grau 3. '''Solução''' Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas. Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso. Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a <pre>Dom3 := [false,false,false], [false,false,true], [false,true,false], [true,false,false], [true,false,true], [true,true,false], [false,true,true], [true,true,true];</pre> Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''. <pre>with(combinat):</pre> Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada <pre>writeto(terminal);</pre> Use a facilidade help do Maple para saber mais sobre essas funções, se necessário. Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los. '''Solução''' Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos. Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado. <pre>with(logic):</pre> Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar <pre>randbool(a,b);</pre> ou <pre>randbool([a,b]);</pre> Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''': <pre>for i from 1 to 10 do randbool(a,b); od;</pre> Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes. <pre>randbool(x,y,z, CNF); randbool(u,v, MOD2);</pre> Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema. A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores. O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada <pre>readlib(trace);</pre> Agora, se gerarmos um expressão aleatória booleana com quatro variáveis <pre>e := randbool(a,b,c,d);</pre> poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo <pre>bsimp(e);</pre> O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui. O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada. Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''': <pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre> Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1. <pre>Printlevel;</pre> Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle. <pre>for i from 1 to 5 do sqrt(i); od;</pre> Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple. Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto. <pre>for i from 1 to 5 do sqrt(sin(abs(i))); od;</pre> Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000. Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados. <pre>with(logic): # esteja certo que 'bsimp' está definida e := x &and y &or &not z; bsimp(e);</pre> Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte <pre>interface(verboseproc = 2); eval(bsimp); # assuma 'bsimp' carregada</pre> Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui). Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas. Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis​​, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média. == '''Exercícios e Projetos''' == 01) Use o Maple para verificar a Lei DeMorgan e as leis de comutatividade e associatividade: .: Leis de DeMorgan :. Equivalent(`&not`(`&or`(A, B)), `&and`(`&not`(A), `&not`(B))); true Equivalent(`&not`(`&and`(A, B)), `&or`(`&not`(A), `&not`(B))); true .: Comutatividade :. Equivalent(`&and`(A, B), `&and`(B, A)); true Equivalent(`&or`(A, B), `&or`(B, A)); true .: Associatividade :. exp1 := `&and`(A, `&and`(B, C)); exp2 := `&and`(`&and`(A, B), C); Equivalent(exp1, exp2); true exp3 := `&or`(A, `&or`(B, C)); exp4 := `&or`(`&or`(A, B), C); Equivalent(exp3, exp4); true 02) Use Maple para construir as tabelas verdades para cada um dos seguintes pares de expressões booleanas. with(Logic): TruthTable(((a &implies b)&and(b &implies a)), [a,b], output = Matrix); table([ (true, true) = true, (false, true) = false, (false, false) = true, (true, false) = false ]) TruthTable(`&and`(`&implies`(a, `&not`(b)), `&implies`(b, `&not`(a))), [a, b], output = Matrix); table([ (true, true) = false, (false, true) = true, (false, false) = true, (true, false) = true ]) TruthTable(`&and`(`&or`(a, `&and`(b, `&not`(c))), (`&or`(`&or`(a, b), c))*(a+c+d)), [a, b, c, d], output = Matrix); table([ (false, false, true, true) = false, (true, false, false, true) = false, (false, false, false, false) = false, (true, false, true, true) = false, (false, false, false, true) = false, (true, true, false, true) = false, (false, false, true, false) = false, (true, true, false, false) = false, (true, false, false, false) = false, (false, true, true, false) = false, (true, true, true, true) = false, (false, true, true, true) = false, (true, true, true, false) = false, (false, true, false, false) = false, (true, false, true, false) = false, (false, true, false, true) = false ]) b37ae61708318abd8886a3343b20ea3704e624f4 573 572 2016-05-28T18:52:06Z Paulohq 21 /* Exercícios e Projetos */ wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem2.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles. Construa a tabela da função booleana de grau 3. '''Solução''' Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas. Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso. Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a <pre>Dom3 := [false,false,false], [false,false,true], [false,true,false], [true,false,false], [true,false,true], [true,true,false], [false,true,true], [true,true,true];</pre> Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''. <pre>with(combinat):</pre> Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada <pre>writeto(terminal);</pre> Use a facilidade help do Maple para saber mais sobre essas funções, se necessário. Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los. '''Solução''' Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos. Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado. <pre>with(logic):</pre> Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar <pre>randbool(a,b);</pre> ou <pre>randbool([a,b]);</pre> Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''': <pre>for i from 1 to 10 do randbool(a,b); od;</pre> Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes. <pre>randbool(x,y,z, CNF); randbool(u,v, MOD2);</pre> Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema. A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores. O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada <pre>readlib(trace);</pre> Agora, se gerarmos um expressão aleatória booleana com quatro variáveis <pre>e := randbool(a,b,c,d);</pre> poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo <pre>bsimp(e);</pre> O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui. O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada. Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''': <pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre> Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1. <pre>Printlevel;</pre> Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle. <pre>for i from 1 to 5 do sqrt(i); od;</pre> Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple. Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto. <pre>for i from 1 to 5 do sqrt(sin(abs(i))); od;</pre> Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000. Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados. <pre>with(logic): # esteja certo que 'bsimp' está definida e := x &and y &or &not z; bsimp(e);</pre> Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte <pre>interface(verboseproc = 2); eval(bsimp); # assuma 'bsimp' carregada</pre> Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui). Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas. Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis​​, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média. == '''Exercícios e Projetos''' == 01) Use o Maple para verificar a Lei DeMorgan e as leis de comutatividade e associatividade: .: Leis de DeMorgan :. Equivalent(`&not`(`&or`(A, B)), `&and`(`&not`(A), `&not`(B))); true Equivalent(`&not`(`&and`(A, B)), `&or`(`&not`(A), `&not`(B))); true .: Comutatividade :. Equivalent(`&and`(A, B), `&and`(B, A)); true Equivalent(`&or`(A, B), `&or`(B, A)); true .: Associatividade :. exp1 := `&and`(A, `&and`(B, C)); exp2 := `&and`(`&and`(A, B), C); Equivalent(exp1, exp2); true exp3 := `&or`(A, `&or`(B, C)); exp4 := `&or`(`&or`(A, B), C); Equivalent(exp3, exp4); true 02) Use Maple para construir as tabelas verdades para cada um dos seguintes pares de expressões booleanas. a) a -> b and b -> a with(Logic): TruthTable(((a &implies b)&and(b &implies a)), [a,b], output = Matrix); table([ (true, true) = true, (false, true) = false, (false, false) = true, (true, false) = false ]) b) a -> ¬b and b -> ¬a with(Logic): TruthTable(`&and`(`&implies`(a, `&not`(b)), `&implies`(b, `&not`(a))), [a, b], output = Matrix); table([ (true, true) = false, (false, true) = true, (false, false) = true, (true, false) = true ]) c) a + (b(¬c)) and (a + b +d)(a + c + d) with(Logic): TruthTable(`&and`(`&or`(a, `&and`(b, `&not`(c))), (`&or`(`&or`(a, b), c))*(a+c+d)), [a, b, c, d], output = Matrix); table([ (false, false, true, true) = false, (true, false, false, true) = false, (false, false, false, false) = false, (true, false, true, true) = false, (false, false, false, true) = false, (true, true, false, true) = false, (false, false, true, false) = false, (true, true, false, false) = false, (true, false, false, false) = false, (false, true, true, false) = false, (true, true, true, true) = false, (false, true, true, true) = false, (true, true, true, false) = false, (false, true, false, false) = false, (true, false, true, false) = false, (false, true, false, true) = false ]) 9d3c2ba43ca25aee3c93cf286653705dc78447fd File:Imagem2.png 6 127 570 2016-05-27T03:46:09Z Clah 19 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Fundamentos Matemáticos da Computação 2 0 125 574 509 2016-05-29T06:52:14Z Gellyviana 31 wikitext text/x-wiki [[Álgebra Booleana]] [[Indução e Recursão]] b85ebcf2bfe622d0d5dbfe07cfa0347436423633 575 574 2016-05-29T06:53:24Z Gellyviana 31 wikitext text/x-wiki [[Álgebra Booleana]] [[Indução e Recursão Matemática]] 768d3d87b977065b5533ad090df945d036b0a0f7 Indução e Recursão Matemática 0 128 576 2016-05-29T06:54:14Z Gellyviana 31 Created page with "== Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais pode..." wikitext text/x-wiki == Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. 481a538d3f9fd1b76d9a67c76bfebbc179c94ceb 577 576 2016-05-29T07:03:20Z Gellyviana 31 wikitext text/x-wiki == Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. #Metodo de Prova Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: 49643f7d841ecf207be1dc9a07d68cdf5727879e 578 577 2016-05-29T07:22:51Z Gellyviana 31 wikitext text/x-wiki == Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. #'''Metodo de Prova''' Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. Então simplificamos a expressão booleana Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. #Indução Matemática O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que n² é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução; neste caso o passo base é n=1 1ac6087079818ca72984eaa1d1f5391c003715a1 579 578 2016-05-29T07:53:45Z Gellyviana 31 wikitext text/x-wiki == Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. #'''Metodo de Prova''' Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. Então simplificamos a expressão booleana Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ##'''Indução Matemática''' O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que n² é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução; neste caso o passo base é n=1 Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. Para somar k+1 termos, computamos: Por fim, a fórmula para n=k+1 é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ###'''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função “->” do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando proc. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ####'''omputações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. #*Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, 'prevprime' e 'ithprime' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. #**Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). #***A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. c859e364526d3515eb8563052aaa6cf27a071ca7 580 579 2016-05-29T08:02:28Z Gellyviana 31 /* Introdução */ wikitext text/x-wiki == Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. #'''Metodo de Prova''' Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. #'''Indução Matemática''' O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que n² é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. #'''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. #'''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. #*Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. #*Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). #*A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. 1f28b6407e9bb1e7b6e5d42a98fbb7df30723ded 581 580 2016-05-29T13:49:57Z Gellyviana 31 wikitext text/x-wiki == Introdução == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que n² é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. 333d672ab43cb4650605017adfc87dd08f8fe1d0 582 581 2016-05-29T13:57:09Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n²</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. 6a75559aca0c074c33aa8888f87a3713030335d4 583 582 2016-05-29T13:58:15Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>$n^2$</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. 3ee15d8cf3eb2c0b6a12c83073b64a73f2bb8519 584 583 2016-05-29T13:59:04Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. 721b195ce992b34d5b7f78064a69bfe8655908b4 585 584 2016-05-29T14:02:03Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>'''N! +1'''</math>, é possível que <math>'''N!+1'''<\math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. b3a3f06a710dd7a9a77c118830da9850a65c20bb 586 585 2016-05-29T14:10:46Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, '''N!+1'''usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC'''como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. a8101e4f6fc7301edc0a3be0bf4754e82c59b696 587 586 2016-05-29T14:13:16Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, '''N!+1'''usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. afafff7a0c3fb59b67ceb775bcbec41b3bc8d6ca 588 587 2016-05-29T14:20:39Z Gellyviana 31 wikitext text/x-wiki \documentclass{article} \begin{document} == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, '''N!+1'''usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. \end{document} fec6cce1ce2afc870d9d8f0da51bc5b5bd24bf0f 589 588 2016-05-29T14:29:12Z Gellyviana 31 wikitext text/x-wiki == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que (\n)^2 é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, '''N!+1'''usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. 54e28fc4765fa25e1e9c1b38193afe4d3a55a920 590 589 2016-05-29T14:31:30Z Gellyviana 31 wikitext text/x-wiki \documentclass[12pt]{article} \usepackage{sbc-template} \usepackage{graphicx,url} \usepackage{float} %\usepackage[brazil]{babel} \usepackage[utf8]{inputenc} == '''Introdução''' == Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==='''Metodo de Prova'''=== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e '''implicação'''. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' '''&''', por exemplo: usamos '''&and''' ao invés de '''and''' e '''&not''' ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: Isto pode ser simplificado através do uso da função bsimp do maple. Agora, seguimos para um exemplo mais complexo,no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. O próximo exemplo ilustra a simplificação do 'Modus Ponens'. Primeiro afirmamos que '''p''' implica '''q''' e '''p''' é verdadeiro. Então simplificamos a expressão booleana Determinando que '''q''' e '''p''' são verdadeiros, como nós já sabíamos que '''p''' era verdadeiro, nós concluímos que '''q''' é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote lógico. Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos Enquanto o maple pode ser usado para gerar uma lista de '''n''' inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo '''n''', é possível usar o maple para encontrar a menor sequencia de '''n''' inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro '''N! +1''', é possível que '''N!+1''' seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando '''N!+1''' diretamente, usando a rotina '''ifactor''' da biblioteca maple. Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: Ela usa o procedimento '''factorset''' do pacote numtheory para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. ==='''Indução Matemática'''=== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula '''1+2+3+...+n = n(n+1)/2''',a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinados. Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que (\n)^2 é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de '''n''', resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c. precisamos de três equações para serem solucionadas em busca dos três coeficientes Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. A nossa fórmula original torna-se: Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: Enquanto o lado direito da fórmula é: Podemos usar o procedimento subs para verificar o passo base da indução, neste caso o passo base é '''n=1''' Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, '''N!+1'''usamos que a fórmula seja válida para '''n=k'''. Para somar '''k+1''' termos, computamos: Por fim, a fórmula para '''n=k+1''' é: Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo O passo base é: O passo indutivo é: Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. '''Definições Recursiva e Interativa''' As funções do maple podem ser definidas tanto processualmente (usando a função '''proc''') como explicitamente (usando a notação '''->'''), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função '''->''' do maple. Se nós quiséssemos definir uma função polinomial '''A(n)= 3n³ + 41n²- 3n + 101''' nós usaríamos o seguinte comando: Agora, se quiséssemos definir uma função recursivamente, digamos: '''b(n) =b(n-1)² + 2b(n-1) +6''', com a condição inicial '''b(0) =2''', então informaríamos('''inserir'''): Se quiséssemos ver uma sequencia de valores para a função '''b''', podemos usar a função '''seq''', para exibir as saídas de um dado intervalo de entradas. Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. Enquanto a notação '''->''' para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção remember para definições de procedimentos afetados pelo uso do proc. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. Esse método processual engloba ambos os casos, o caso base (quando '''n <=2''') e os casos indutivos (como na condição “else”). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e '''->''' usando a função time do maple. Então , fica claro que a função remember pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um 'for' ou 'while') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como 'top-down', pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para '''f2(100)'''. De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente '''f2(99)''' chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. '''Computações e Explorações''' Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. *Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote '''numtheory''' do maple, que contém as funções nextprime, '''prevprime''' e '''ithprime''' Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i’s''' que formarem pares. Parece não haver um padrão óbvio ocorrendo. *Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne “verdadeiro”. Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci '''Fn''' é divisível por 5, somente se '''n''' é divisível por 5. Para obter evidências para a conversão, devemos testar se '''F5''' '''n''' é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, amenos que seja encontrado um contra exemplo. Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. Podemos perceber um padrão nesses dados, como o seguinte: Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (o teste da divisibilidade por 111, nós deixamos pra você). *A notória conjectura '''3x + 1''' (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função '''f(x)''', onde '''f(x) = x/2''', se '''x''' é par e '''f(x) = 3x+1''' se '''x''' é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável 'count' por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada 'seed', nos codificamos um limite superior para o número de iterações a serem computadas. Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. Para inicar, precisamos definir a função, a qual examinaremos. \end{document} 57fdb782fbf8d32125f9232431ed6a3f551a1af6 File:Imagem30.png 6 129 591 2016-05-29T16:09:11Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem31.png 6 130 592 2016-05-29T16:09:12Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem32.png 6 131 593 2016-05-29T16:09:12Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem33.png 6 132 594 2016-05-29T16:09:12Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem34.png 6 133 595 2016-05-29T16:09:12Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem35.png 6 134 596 2016-05-29T16:09:13Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem36.png 6 135 597 2016-05-29T16:09:13Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem37.png 6 136 598 2016-05-29T16:09:13Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem38.png 6 137 599 2016-05-29T16:09:13Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem39.png 6 138 600 2016-05-29T16:09:13Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem40.png 6 139 601 2016-05-29T16:09:13Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem41.png 6 140 602 2016-05-29T16:09:14Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem42.png 6 141 603 2016-05-29T16:09:14Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem43.png 6 142 604 2016-05-29T16:09:14Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem44.png 6 143 605 2016-05-29T16:09:14Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem45.png 6 144 606 2016-05-29T16:09:15Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem46.png 6 145 607 2016-05-29T16:09:15Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem47.png 6 146 608 2016-05-29T16:09:15Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem48.png 6 147 609 2016-05-29T16:09:16Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem49.png 6 148 610 2016-05-29T16:09:16Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem50.png 6 149 611 2016-05-29T16:09:16Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem51.png 6 150 612 2016-05-29T16:09:17Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem52.png 6 151 613 2016-05-29T16:09:17Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagens.png 6 152 614 2016-05-29T16:09:17Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Indução e Recursão Matemática 0 128 615 590 2016-05-29T16:10:35Z Marcielmanoel15 30 wikitext text/x-wiki %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % How to use writeLaTeX: % % You edit the source code here on the left, and the preview on the % right shows you the result within a few seconds. % % Bookmark this page and share the URL with your co-authors. They can % edit at the same time! % % You can upload figures, bibliographies, custom classes and % styles using the files menu. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \documentclass[12pt]{article} \usepackage{sbc-template} \usepackage{graphicx,url} \usepackage{float} %\usepackage[brazil]{babel} \usepackage[utf8]{inputenc} \sloppy \title{Indução e Recursão Matemática\\ Fundamentos Matemáticos da Computação II.} \author{Gelly V. Mota\inst{1}, Igor B. Nogueira\inst{1}, Rafael dos S. Bezerra\inst{1},Welligton M. da Silva\inst{1}} \address{Discentes do Instituto Metrópole Digital -- Universidade Federal do Rio Grande do Norte \email{gellyviana@outlook.com, (igornogueir, rafael.sbmail, miguelwelligton)@gmail.com} } \begin{document} \maketitle \begin{resumo} Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . \end{resumo} \section{Introdução} Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. \section{Método de prova} Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem1.png} \end{figure} Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem2.png} \end{figure} Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem3.png} \end{figure} Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem4.png} \end{figure} O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem5.png} \end{figure} Então simplificamos a expressão booleana, \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem6.png} \end{figure} Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função {\bf bsimp} é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem7.png} \end{figure} Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem8.png} \end{figure} Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina {\bf ifactor} da biblioteca maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem9.png} \end{figure} Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem10.png} \end{figure} Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem11.png} \end{figure} Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem12.png} \end{figure} Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem13.png} \end{figure} \section{Indução Matemática} O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem14.png} \end{figure} Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem15.png} \end{figure} Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem16.png} \end{figure} Precisamos de três equações para serem solucionadas em busca dos três coeficientes: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem17.png} \end{figure} Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem18.png} \end{figure} A nossa fórmula original torna-se: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem19.png} \end{figure} Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem20.png} \end{figure} Enquanto o lado direito da fórmula é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem21.png} \end{figure} Podemos usar o procedimento {\bf subs} para verificar o passo base da indução; neste caso o passo base é $n=1$ \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem22.png} \end{figure} Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem23.png} \end{figure} Para somar k+1 termos, computamos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem.png} \end{figure} Por fim, a fórmula para n=k+1 é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem24.png} \end{figure} Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem25.png} \end{figure} Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem26.png} \end{figure} Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagens.png} \end{figure} Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem28.png} \end{figure} O passo base é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{entrenseind.png} \end{figure} O passo indutivo é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem29.png} \end{figure} Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. \section{Definições Recursiva e Interativa} As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem30.png} \end{figure} Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem31.png} \end{figure} Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem32.png} \end{figure} Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem33.png} \end{figure} Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem34.png} \end{figure} Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem35.png} \end{figure} Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem36.png} \end{figure} Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem37.png} \end{figure} Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem38.png} \end{figure} Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem39.png} \end{figure} Que é comparável às vezes obtidas para {\bf f2}. Note que a implementação puramente recursiva {\bf f1} não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, {\bf f2} precisaria de aproximadamente \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem40.png} \end{figure} Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. \section{Computações e Explorações} Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções {\bf nextprime}, {\bf prevprime} e {\bf ithprime} \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem41.png} \end{figure} Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem42.png} \end{figure} Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem43.png} \end{figure} Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem44.png} \end{figure} Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem45.png} \end{figure} Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem46.png} \end{figure} Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem47.png} \end{figure} Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem48.png} \end{figure} Podemos perceber um padrão nesses dados, como o seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem49.png} \end{figure} Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem50.png} \end{figure} Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem51.png} \end{figure} Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem52.png} \end{figure} Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} \section{Conclusão}\label{sec:figs} A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . \section{References} O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. \bibliographystyle{sbc} \bibliography{sbc-template} \end{document} 932911900346fa7bcd6dfd814228db10fdfa7476 616 615 2016-05-29T16:12:42Z Marcielmanoel15 30 wikitext text/x-wiki %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % How to use writeLaTeX: % % You edit the source code here on the left, and the preview on the % right shows you the result within a few seconds. % % Bookmark this page and share the URL with your co-authors. They can % edit at the same time! % % You can upload figures, bibliographies, custom classes and % styles using the files menu. % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% \documentclass[12pt]{article} \usepackage{sbc-template} \usepackage{graphicx,url} \usepackage{float} %\usepackage[brazil]{babel} \usepackage[utf8]{inputenc} \sloppy \title{Indução e Recursão Matemática\\ Fundamentos Matemáticos da Computação II.} \author{Gelly V. Mota\inst{1}, Igor B. Nogueira\inst{1}, Rafael dos S. Bezerra\inst{1},Welligton M. da Silva\inst{1}} \address{Discentes do Instituto Metrópole Digital -- Universidade Federal do Rio Grande do Norte \email{gellyviana@outlook.com, (igornogueir, rafael.sbmail, miguelwelligton)@gmail.com} } \begin{document} \maketitle \begin{resumo} Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . \end{resumo} \section{Introdução} Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. \section{Método de prova} Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem2.png} \end{figure} Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem3.png} \end{figure} Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem4.png} \end{figure} O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem5.png} \end{figure} Então simplificamos a expressão booleana, \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem6.png} \end{figure} Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função {\bf bsimp} é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem7.png} \end{figure} Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem8.png} \end{figure} Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina {\bf ifactor} da biblioteca maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem9.png} \end{figure} Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem10.png} \end{figure} Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem11.png} \end{figure} Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem12.png} \end{figure} Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem13.png} \end{figure} \section{Indução Matemática} O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem14.png} \end{figure} Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem15.png} \end{figure} Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem16.png} \end{figure} Precisamos de três equações para serem solucionadas em busca dos três coeficientes: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem17.png} \end{figure} Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem18.png} \end{figure} A nossa fórmula original torna-se: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem19.png} \end{figure} Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem20.png} \end{figure} Enquanto o lado direito da fórmula é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem21.png} \end{figure} Podemos usar o procedimento {\bf subs} para verificar o passo base da indução; neste caso o passo base é $n=1$ \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem22.png} \end{figure} Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem23.png} \end{figure} Para somar k+1 termos, computamos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem.png} \end{figure} Por fim, a fórmula para n=k+1 é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem24.png} \end{figure} Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem25.png} \end{figure} Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem26.png} \end{figure} Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagens.png} \end{figure} Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem28.png} \end{figure} O passo base é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{entrenseind.png} \end{figure} O passo indutivo é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem29.png} \end{figure} Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. \section{Definições Recursiva e Interativa} As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem30.png} \end{figure} Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem31.png} \end{figure} Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem32.png} \end{figure} Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem33.png} \end{figure} Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem34.png} \end{figure} Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem35.png} \end{figure} Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem36.png} \end{figure} Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem37.png} \end{figure} Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem38.png} \end{figure} Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem39.png} \end{figure} Que é comparável às vezes obtidas para {\bf f2}. Note que a implementação puramente recursiva {\bf f1} não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, {\bf f2} precisaria de aproximadamente \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem40.png} \end{figure} Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. \section{Computações e Explorações} Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções {\bf nextprime}, {\bf prevprime} e {\bf ithprime} \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem41.png} \end{figure} Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem42.png} \end{figure} Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem43.png} \end{figure} Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem44.png} \end{figure} Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem45.png} \end{figure} Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem46.png} \end{figure} Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem47.png} \end{figure} Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem48.png} \end{figure} Podemos perceber um padrão nesses dados, como o seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem49.png} \end{figure} Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem50.png} \end{figure} Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem51.png} \end{figure} Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem52.png} \end{figure} Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} \section{Conclusão}\label{sec:figs} A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . \section{References} O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. \bibliographystyle{sbc} \bibliography{sbc-template} \end{document} f42ce00ed7ab5440a7a49ccf1608779025d691f2 617 616 2016-05-29T16:16:36Z Marcielmanoel15 30 wikitext text/x-wiki =Indução e Recursão Matemática= Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem3.png} \end{figure} Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem4.png} \end{figure} O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem5.png} \end{figure} Então simplificamos a expressão booleana, \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem6.png} \end{figure} Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função {\bf bsimp} é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem7.png} \end{figure} Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem8.png} \end{figure} Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina {\bf ifactor} da biblioteca maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem9.png} \end{figure} Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem10.png} \end{figure} Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem11.png} \end{figure} Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem12.png} \end{figure} Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem13.png} \end{figure} \section{Indução Matemática} O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem14.png} \end{figure} Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem15.png} \end{figure} Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem16.png} \end{figure} Precisamos de três equações para serem solucionadas em busca dos três coeficientes: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem17.png} \end{figure} Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem18.png} \end{figure} A nossa fórmula original torna-se: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem19.png} \end{figure} Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem20.png} \end{figure} Enquanto o lado direito da fórmula é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem21.png} \end{figure} Podemos usar o procedimento {\bf subs} para verificar o passo base da indução; neste caso o passo base é $n=1$ \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem22.png} \end{figure} Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem23.png} \end{figure} Para somar k+1 termos, computamos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem.png} \end{figure} Por fim, a fórmula para n=k+1 é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem24.png} \end{figure} Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem25.png} \end{figure} Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem26.png} \end{figure} Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagens.png} \end{figure} Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem28.png} \end{figure} O passo base é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{entrenseind.png} \end{figure} O passo indutivo é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem29.png} \end{figure} Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. \section{Definições Recursiva e Interativa} As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem30.png} \end{figure} Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem31.png} \end{figure} Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem32.png} \end{figure} Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem33.png} \end{figure} Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem34.png} \end{figure} Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem35.png} \end{figure} Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem36.png} \end{figure} Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem37.png} \end{figure} Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem38.png} \end{figure} Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem39.png} \end{figure} Que é comparável às vezes obtidas para {\bf f2}. Note que a implementação puramente recursiva {\bf f1} não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, {\bf f2} precisaria de aproximadamente \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem40.png} \end{figure} Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. \section{Computações e Explorações} Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções {\bf nextprime}, {\bf prevprime} e {\bf ithprime} \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem41.png} \end{figure} Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem42.png} \end{figure} Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem43.png} \end{figure} Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem44.png} \end{figure} Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem45.png} \end{figure} Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem46.png} \end{figure} Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem47.png} \end{figure} Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem48.png} \end{figure} Podemos perceber um padrão nesses dados, como o seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem49.png} \end{figure} Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem50.png} \end{figure} Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem51.png} \end{figure} Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem52.png} \end{figure} Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} \section{Conclusão}\label{sec:figs} A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . \section{References} O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. \bibliographystyle{sbc} \bibliography{sbc-template} \end{document} 14a411d69ab7679fd01afa89fd94049c7cd15c6d 639 617 2016-05-29T16:22:11Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem5.png} \end{figure} Então simplificamos a expressão booleana, \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem6.png} \end{figure} Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função {\bf bsimp} é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem7.png} \end{figure} Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem8.png} \end{figure} Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina {\bf ifactor} da biblioteca maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem9.png} \end{figure} Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem10.png} \end{figure} Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem11.png} \end{figure} Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem12.png} \end{figure} Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem13.png} \end{figure} \section{Indução Matemática} O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem14.png} \end{figure} Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem15.png} \end{figure} Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem16.png} \end{figure} Precisamos de três equações para serem solucionadas em busca dos três coeficientes: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem17.png} \end{figure} Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem18.png} \end{figure} A nossa fórmula original torna-se: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem19.png} \end{figure} Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem20.png} \end{figure} Enquanto o lado direito da fórmula é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem21.png} \end{figure} Podemos usar o procedimento {\bf subs} para verificar o passo base da indução; neste caso o passo base é $n=1$ \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem22.png} \end{figure} Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem23.png} \end{figure} Para somar k+1 termos, computamos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem.png} \end{figure} Por fim, a fórmula para n=k+1 é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem24.png} \end{figure} Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem25.png} \end{figure} Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem26.png} \end{figure} Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagens.png} \end{figure} Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem28.png} \end{figure} O passo base é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{entrenseind.png} \end{figure} O passo indutivo é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem29.png} \end{figure} Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. \section{Definições Recursiva e Interativa} As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem30.png} \end{figure} Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem31.png} \end{figure} Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem32.png} \end{figure} Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem33.png} \end{figure} Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem34.png} \end{figure} Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem35.png} \end{figure} Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem36.png} \end{figure} Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem37.png} \end{figure} Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem38.png} \end{figure} Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem39.png} \end{figure} Que é comparável às vezes obtidas para {\bf f2}. Note que a implementação puramente recursiva {\bf f1} não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, {\bf f2} precisaria de aproximadamente \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem40.png} \end{figure} Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. \section{Computações e Explorações} Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções {\bf nextprime}, {\bf prevprime} e {\bf ithprime} \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem41.png} \end{figure} Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem42.png} \end{figure} Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem43.png} \end{figure} Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem44.png} \end{figure} Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem45.png} \end{figure} Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem46.png} \end{figure} Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem47.png} \end{figure} Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem48.png} \end{figure} Podemos perceber um padrão nesses dados, como o seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem49.png} \end{figure} Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem50.png} \end{figure} Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem51.png} \end{figure} Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem52.png} \end{figure} Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} \section{Conclusão}\label{sec:figs} A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . \section{References} O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. \bibliographystyle{sbc} \bibliography{sbc-template} \end{document} dec1b5743b6874fe0e899fc0f1249f53293db246 640 639 2016-05-29T16:26:29Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem5.png} \end{figure} Então simplificamos a expressão booleana, \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem6.png} \end{figure} Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função {\bf bsimp} é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem7.png} \end{figure} Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem8.png} \end{figure} Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina {\bf ifactor} da biblioteca maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem9.png} \end{figure} Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem10.png} \end{figure} Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem11.png} \end{figure} Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem12.png} \end{figure} Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem13.png} \end{figure} \section{Indução Matemática} O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem14.png} \end{figure} Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem15.png} \end{figure} Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem16.png} \end{figure} Precisamos de três equações para serem solucionadas em busca dos três coeficientes: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem17.png} \end{figure} Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem18.png} \end{figure} A nossa fórmula original torna-se: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem19.png} \end{figure} Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem20.png} \end{figure} Enquanto o lado direito da fórmula é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem21.png} \end{figure} Podemos usar o procedimento {\bf subs} para verificar o passo base da indução; neste caso o passo base é $n=1$ \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem22.png} \end{figure} Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem23.png} \end{figure} Para somar k+1 termos, computamos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem.png} \end{figure} Por fim, a fórmula para n=k+1 é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem24.png} \end{figure} Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem25.png} \end{figure} Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem26.png} \end{figure} Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagens.png} \end{figure} Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem28.png} \end{figure} O passo base é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{entrenseind.png} \end{figure} O passo indutivo é: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem29.png} \end{figure} Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. \section{Definições Recursiva e Interativa} As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem30.png} \end{figure} Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem31.png} \end{figure} Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem32.png} \end{figure} Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem33.png} \end{figure} Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem34.png} \end{figure} Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem35.png} \end{figure} Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem36.png} \end{figure} Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem37.png} \end{figure} Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem38.png} \end{figure} Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem39.png} \end{figure} Que é comparável às vezes obtidas para {\bf f2}. Note que a implementação puramente recursiva {\bf f1} não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, {\bf f2} precisaria de aproximadamente \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem40.png} \end{figure} Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. \section{Computações e Explorações} Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções {\bf nextprime}, {\bf prevprime} e {\bf ithprime} \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem41.png} \end{figure} Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem42.png} \end{figure} Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem43.png} \end{figure} Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). \begin{figure}[H] \centering \includegraphics[width=1.0\textwidth]{imagem44.png} \end{figure} Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. cbb5be67529abaa54d48f5aa51fe4d8100d5a7c8 641 640 2016-05-29T16:39:28Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é $n=1$ [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 144c50b8d388085f4b5688fd64169794bd63c4cc 654 641 2016-05-29T16:43:04Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\&{\bf not}$ ao invés de {\bf not}. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função {\bf bsimp} do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é $n=1$ [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. be961ad3255893ed11cb6b5d0ed1076e4737723d 655 654 2016-05-29T16:45:29Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e {\bf implicação}. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote de {\bf logic}. Todos esses são iniciados com o caracter {\bf mexpr} $\&$ , por exemplo: usamos $\&{\bf and}$ ao invés de {\bf and} e $\'''&not'''$ ao invés de '''not'''. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula $1+2+3+...+n = n(n+1)/2$,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é $n=1$ [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. $S = 1.1! + 2.2! +$...$+n.n!$ É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: $S=(n+1)!-1$ A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação $->$), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função $->$ do maple. Se nós quiséssemos definir uma função polinomial $A(n)= 3n^3 + 41n^2- 3n + 101$ nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: $b(n) =b(n-1)^2 + 2b(n-1) +6$, com a condição inicial $b(0) =2$, então informaríamos(inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a {\bf b}, chamada {\bf f1}, que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação “->” para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando {\bf proc}. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção {\bf remember} para definições de procedimentos afetados pelo uso do {\bf proc}. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando n <=2) e os casos indutivos (como na condição {\bf else}). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci $F_n$ é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se $F_5n$ é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo $500$ na definição de {\bf fib\_list} por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura $3x + 1$ (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 6f96a07aeb1620a121f79498acf0d5c60e88c448 656 655 2016-05-29T16:55:37Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>&</math> , por exemplo: usamos <math>'''&and'''</math> ao invés de <math>'''and'''</math> e <math>'''&not'''</math> ao invés de <math>'''not'''</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 88b8a7537e8468846b3b4ad10b9f541a1b7d2791 657 656 2016-05-29T16:57:26Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>&</math> , por exemplo: usamos <math>&and</math> invés de <math>and</math> e <math>&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. dab88e984df9182452c3238b5f948c41a94b4f68 658 657 2016-05-29T16:58:33Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. cca8c1c208f13c5abaf614d0b1b8ffbb67a3e0f8 659 658 2016-05-29T16:59:49Z Marcielmanoel15 30 /* Indução Matemática */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. f251eeb2afa110676ee073e208849aa651f137c5 660 659 2016-05-29T17:00:23Z Marcielmanoel15 30 /* Indução Matemática */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. \subsection{Exercícios e Projetos} ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. c5e279726e98bf6a10f7fa368b48596359a73a9c 661 660 2016-05-29T17:02:48Z Marcielmanoel15 30 /* Computações e Explorações */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de {\bf logic}(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. e49e5fac889e11ae1436719f2e581a89b0f337df 662 661 2016-05-29T17:03:47Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 54db788b1eb2c75b78ca4c8f97a6ae0b6f226c9e File:Entrenseind.png 6 153 618 2016-05-29T16:21:03Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem1.png 6 154 619 2016-05-29T16:21:03Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem3.png 6 155 620 2016-05-29T16:21:03Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 644 620 2016-05-29T16:41:38Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem3.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem4.png 6 156 621 2016-05-29T16:21:03Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 645 621 2016-05-29T16:41:38Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem4.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem5.png 6 157 622 2016-05-29T16:21:04Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem6.png 6 158 623 2016-05-29T16:21:04Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem7.png 6 159 624 2016-05-29T16:21:04Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem8.png 6 160 625 2016-05-29T16:21:05Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem9.png 6 161 626 2016-05-29T16:21:05Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem10.png 6 162 627 2016-05-29T16:21:05Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem11.png 6 163 628 2016-05-29T16:21:05Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem12.png 6 164 629 2016-05-29T16:21:06Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem13.png 6 165 630 2016-05-29T16:21:06Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem14.png 6 166 631 2016-05-29T16:21:06Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem15.png 6 167 632 2016-05-29T16:21:06Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem16.png 6 168 633 2016-05-29T16:21:06Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem17.png 6 169 634 2016-05-29T16:21:07Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem18.png 6 170 635 2016-05-29T16:21:07Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem19.png 6 171 636 2016-05-29T16:21:07Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem20.png 6 172 637 2016-05-29T16:21:07Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem21.png 6 173 638 2016-05-29T16:21:08Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem.png 6 126 642 562 2016-05-29T16:41:37Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem2.png 6 127 643 570 2016-05-29T16:41:38Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem22.png 6 174 646 2016-05-29T16:41:38Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem23.png 6 175 647 2016-05-29T16:41:39Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem24.png 6 176 648 2016-05-29T16:41:39Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem25.png 6 177 649 2016-05-29T16:41:39Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem26.png 6 178 650 2016-05-29T16:41:39Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem27.png 6 179 651 2016-05-29T16:41:39Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem28.png 6 180 652 2016-05-29T16:41:40Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem29.png 6 181 653 2016-05-29T16:41:40Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Indução e Recursão Matemática 0 128 663 662 2016-05-29T17:04:29Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic'''(um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. a0c0b61f0d64924f4c21ec3801216971f960985d 664 663 2016-05-29T17:05:11Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. f02805c926d4344e4c4e1955b87e740241e70b42 665 664 2016-05-29T17:05:55Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro $N! +1$, é possível que $N!+1$ seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando $N!+1$ diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento {\bf factorset} do pacote {\bf numtheory} para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. c42677220bc8535ef86c6459b77d896ceeb8b619 666 665 2016-05-29T17:08:48Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. d5660ff541c8721dd4c61a8da6bdcc5a047351d8 667 666 2016-05-29T17:10:24Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 3a404760d55c563f57688a9e74526cd9e0cd5870 668 667 2016-05-29T17:10:48Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que $n^2$ é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +$...$+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. d3b04796b5961af0c7ef52f77e1a2dacb13c2012 669 668 2016-05-29T17:12:01Z Marcielmanoel15 30 /* Indução Matemática */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 52520a65f5b151d013a204c15a989b7f048dbb34 670 669 2016-05-29T17:12:53Z Marcielmanoel15 30 /* Indução Matemática */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para n=k. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 8abb066c03af3101f1d8d56a9d28b9f7f7ec4d6a 671 670 2016-05-29T17:13:37Z Marcielmanoel15 30 /* Indução Matemática */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:image33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 041d9cebd3a06cf41bd4a084bd19d24f18b2bcf8 674 671 2016-05-29T17:17:14Z Marcielmanoel15 30 /* Definições Recursiva e Interativa */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação <math>-></math>), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função <math>-></math> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 648b26b633455c820985181a9a43fdaea455c52f 675 674 2016-05-29T17:18:02Z Marcielmanoel15 30 /* Definições Recursiva e Interativa */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função {\bf time} do maple. [[File:imagem36.png]] Então , fica claro que a função {\bf remember} pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um {\bf for} ou {\bf while}) que computará valores começando do menor para o maior. Este método de programação é chamado de {\bf bottom up}: onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo {\bf f2} que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. c37e09aca9f6d8d96c4b37167ae8910198e9aebc 676 675 2016-05-29T17:20:03Z Marcielmanoel15 30 /* Definições Recursiva e Interativa */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos {\bf i}’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne {\bf verdadeiro}. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função {\bf IC} como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 078cdfc2c21758b49c0581af34dacdd608c5a5f1 677 676 2016-05-29T17:21:18Z Marcielmanoel15 30 /* Computações e Explorações */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferentepode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 59de39d8bfe73a6aec3c34ffe222aa9e5bcc6ee7 678 677 2016-05-29T17:22:54Z Marcielmanoel15 30 /* Computações e Explorações */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_5n</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 7793ea0a030f307b807909a9a47cf5564af35235 679 678 2016-05-29T17:25:54Z Marcielmanoel15 30 /* Computações e Explorações */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. bbe56cf9845c368aceaa81840f425c16d982c544 680 679 2016-05-29T17:26:30Z Marcielmanoel15 30 /* Computações e Explorações */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 151f272c2f3eb6bc2a8f9c9300a1cb11384d718e 681 680 2016-05-29T17:27:13Z Marcielmanoel15 30 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:imagem2.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 2727300c5df69156673f379506b645e4a1ab4f42 689 681 2016-05-29T17:36:02Z Marcielmanoel15 30 wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math>,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 1c026b40966b1957132c3bd554444b1984ab5163 690 689 2016-05-29T17:47:08Z Gellyviana 31 /* Indução Matemática */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. eac5056885df29e9a5c199912a30b918cb79b10b 691 690 2016-05-29T17:50:29Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 2e8075eeeaebcca8eef0957de8d7660c10dcd50b 692 691 2016-05-29T18:04:20Z Gellyviana 31 /* Computações e Explorações */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 74c1bd711c15d305ee0835c9010fc29e6d18e269 693 692 2016-05-29T18:14:17Z Gellyviana 31 /* Método de prova */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== O desenvolvimento desse trabalho é proviniente da página Online Learning Center do \cite{boulic:91}, sendo a tradução do mesmo. 3d87dfa129e6b53b1c076356f563de3e4f880b41 694 693 2016-05-29T18:21:11Z Marcielmanoel15 30 /* Referências */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 41d959ae74d4689bd01f09ded987e2756478758d 697 694 2016-05-29T18:47:11Z Gellyviana 31 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo n \ge N. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1 ~ mod ~ m</math>,<math> a_2 mod m</math>, <math>a_3 mod m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. a2387c2698ca17b6e0ce25ff72f653bf38368283 699 697 2016-05-29T18:49:03Z Gellyviana 31 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo n \ge N. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1 mod m</math> ,<math> a_2 mod m</math>, <math>a_3 mod m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 260e631ae1b8688d8d62fe0fc8431d893f73e761 700 699 2016-05-29T18:51:42Z Gellyviana 31 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 2e6b1fa6eb1611dba75ee9e0f3db048512c88eca 703 700 2016-05-29T19:00:34Z Gellyviana 31 /*Extra*/ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. f763221561a2627651b7fd21b6d8761259b7320a 712 703 2016-05-29T20:04:51Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = 5n+1 -1/4 </math> para todo <math> n /ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/div 4</math> '''Passo base:''' P(0) <math>1= 50+1 -1/div 4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k)/cdotP(k+1)</math>: suponha para qualquer k, P(k) é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5/cdotk+1 -1/div 4</math> Precisamos mostrar que a próxima afirmação P(k+1), é verdadeira: <math>1+5+5^2 +5^3 +...+5/cdotk+1 = 5/cdotk+2 -1/div 4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5/cdotk+1</math>, em ambos os lados, depois mostramos que essa é a afirmação p(k+1). <math>1+5^2+5^3+...+5k = 5/cdotk+1 -1/div 4</math> ---------------<math>+5/cdotk+1 = 5/cdotk+1 </math>-------- <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /div 4</math> <math>= (1+4)5k+1 -1 /div 4</math> <math>= 5/cdot 5k+1 -1/div4</math> <math>= 5k+2 -1 /div 4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k)/cdotp(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática , p(n) é verdadeiro para todo <math>n/ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que p(k) é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5/cdotk+1 = 5/cdotk+1 -1 /div 4 + 5/cdotk+1</math> <math> = 5k+1 -1 + 4/cdot5k+1</math> <math>= (1+4) 5k+1 -1/div 4</math> <math>=5/cdot 5k+1-1/div 4</math> <math>=5k+2-1/div 4</math> *Exemplo 2 Prove que : <math>/sum limits_{k=1}^n (2k+3)$= n(n+4) para todo n/ge1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n/cdot(n+4)</math> '''Passo base:''' p(1) afirma que <math>2/cdot 1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k)/cdotp(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos (2k+3) a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k/cdot(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1)/cdot(k+5)</math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. abdd4926e98d2ce6d25bc25b84bd5b789a9cf34f File:Imagem.png 6 126 672 642 2016-05-29T17:14:16Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem.png]]&quot;: Reverted to version as of 03:21, 27 May 2016 wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 695 672 2016-05-29T18:21:32Z Gellyviana 31 Gellyviana uploaded a new version of &quot;[[File:Imagem.png]]&quot; wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Imagem2.png 6 127 673 643 2016-05-29T17:16:04Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot;: Reverted to version as of 03:46, 27 May 2016 wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 682 673 2016-05-29T17:29:34Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 683 682 2016-05-29T17:30:14Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot; wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 684 683 2016-05-29T17:31:42Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot; wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 685 684 2016-05-29T17:33:10Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot;: Reverted to version as of 03:46, 27 May 2016 wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e 686 685 2016-05-29T17:34:51Z Marcielmanoel15 30 Marcielmanoel15 uploaded a new version of &quot;[[File:Imagem2.png]]&quot;: MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Image33.png 6 182 687 2016-05-29T17:34:52Z Gellyviana 31 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Capeta.png 6 183 688 2016-05-29T17:35:48Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Fundamentos Matemáticos da Computação 2 0 125 696 575 2016-05-29T18:41:22Z Paulohq 21 wikitext text/x-wiki [[Álgebra Booleana]] [[Árvores]] [[Indução e Recursão Matemática]] 96a09e2c8b2e13933bad69b48744bd6d627c6e58 Árvores 0 184 698 2016-05-29T18:47:13Z Paulohq 21 Created page with "Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circ..." wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: [http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html] junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. 81877eeb97eb1645ac1a0aebfeedf6cd7be021a5 701 698 2016-05-29T18:53:08Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> be8c012363b47493768e7422198f95db0c1446c8 702 701 2016-05-29T18:55:12Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> 8c67e67df2f612e387a24537628de5615482f504 704 702 2016-05-29T19:04:40Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> ==='''Árvores com raiz'''=== Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> bfd365ac8a38a65b58326bc2d9f44169d25752be 705 704 2016-05-29T19:11:22Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> ==='''Árvores com raiz'''=== Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == == Percursos em Árvore == == Árvores e Ordenação == == Árvores de Extensão == == Árvores de Extensão Mínima == == Computações e Explorações == e3432a5cd3837f8acddb6f45f1d085758d99f463 706 705 2016-05-29T19:27:13Z Paulohq 21 /* Aplicação de Árvores */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> ==='''Árvores com raiz'''=== Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. ==='''Inserção binária'''=== Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> ==='''Codificação de Huffman'''=== A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == == Árvores e Ordenação == == Árvores de Extensão == == Árvores de Extensão Mínima == == Computações e Explorações == d2cb5f9676416140d6f64b8f304748b10cebacf5 707 706 2016-05-29T19:29:32Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == == Árvores e Ordenação == == Árvores de Extensão == == Árvores de Extensão Mínima == == Computações e Explorações == 8de533651a2db663f2917e574a41916a7576174c 708 707 2016-05-29T19:40:53Z Paulohq 21 /* Percursos em Árvore */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == == Árvores de Extensão == == Árvores de Extensão Mínima == == Computações e Explorações == 38eab80d5bf21e7ace363fe581863f208d9cc0e8 709 708 2016-05-29T19:46:20Z Paulohq 21 /* Árvores e Ordenação */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == == Árvores de Extensão Mínima == == Computações e Explorações == efc8298ffdf7a88c7fd989ee902bf4bf0e2e9eb7 710 709 2016-05-29T19:58:26Z Paulohq 21 /* Árvores de Extensão */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == == Computações e Explorações == 7e5cc857387ac7bc4098a1e43e4f34a58a3fcf0e 711 710 2016-05-29T20:04:37Z Paulohq 21 /* Árvores de Extensão Mínima */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 5cdf14b76d9b060ef489c24344ee7cd1b13c47d1 Indução e Recursão Matemática 0 128 713 712 2016-05-29T20:07:33Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = 5n+1 -1/4 </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 50+1 -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k).P(k+1)</math>: suponha para qualquer k, P(k) é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5/cdotk+1 -1/div 4</math> Precisamos mostrar que a próxima afirmação P(k+1), é verdadeira: <math>1+5+5^2 +5^3 +...+5/cdotk+1 = 5/cdotk+2 -1/div 4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5/cdotk+1</math>, em ambos os lados, depois mostramos que essa é a afirmação p(k+1). <math>1+5^2+5^3+...+5k = 5/cdotk+1 -1/div 4</math> ---------------<math>+5/cdotk+1 = 5/cdotk+1 </math>-------- <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /div 4</math> <math>= (1+4)5k+1 -1 /div 4</math> <math>= 5/cdot 5k+1 -1/div4</math> <math>= 5k+2 -1 /div 4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k)/cdotp(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática , p(n) é verdadeiro para todo <math>n/ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que p(k) é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5/cdotk+1 = 5/cdotk+1 -1 /div 4 + 5/cdotk+1</math> <math> = 5k+1 -1 + 4/cdot5k+1</math> <math>= (1+4) 5k+1 -1/div 4</math> <math>=5/cdot 5k+1-1/div 4</math> <math>=5k+2-1/div 4</math> *Exemplo 2 Prove que : <math>/sum limits_{k=1}^n (2k+3)$= n(n+4) para todo n/ge1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n/cdot(n+4)</math> '''Passo base:''' p(1) afirma que <math>2/cdot 1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k)/cdotp(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos (2k+3) a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k/cdot(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1)/cdot(k+5)</math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 27422cb8327d1e5a1b2f097fe5bebcbaf0170fed 715 713 2016-05-29T20:17:23Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = 5n+1 -1/4 </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 50+1 -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k).P(k+1)</math>: suponha para qualquer k, P(k) é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação P(k+1), é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação p(k+1). <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> ---------------<math>+5.k+1 = 5.k+1 </math>-------- <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática , p(n) é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que p(k) é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits_{k=1}^n (2k+3)$= n(n+4) para todo n/ge1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n/cdot(n+4)</math> '''Passo base:''' p(1) afirma que <math>2/cdot 1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k)/cdotp(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos (2k+3) a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k/cdot(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1)/cdot(k+5)</math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. bc9863a416ae42c2244ac2dbd12ac9c08d2eb14b 721 715 2016-05-29T20:47:33Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = 5n+1 -1/4 </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 5^(0+1) -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k).P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> <math>+5.k+1 = 5.k+1 </math> <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 6e3152ed2b58ab9a43786940537e1924023f78b8 722 721 2016-05-29T21:01:50Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = frac{5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 5^(0+1) -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k)\toP(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> <math>+5.k+1 = 5.k+1 </math> <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 7722357f4c409acbc471435088faa1369320579f 723 722 2016-05-29T21:04:33Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = <math>frac{5^{n+1}-1}{4}</math> </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 5^(0+1) -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k)\toP(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> <math>+5.k+1 = 5.k+1 </math> <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 124eb4baa55ec0497e83c494059239d13de34ef9 724 723 2016-05-29T21:05:44Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 5^(0+1) -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k)\to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> <math>+5.k+1 = 5.k+1 </math> <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. ea3fb5737caff7e347d59b1831a6b873d6c339b1 725 724 2016-05-29T21:11:33Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = {5^{n+1}-1}/4 </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= 5^(0+1) -1/4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k)\to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> <math>+5.k+1 = 5.k+1 </math> <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 1b4a83f63d086701ac863c8f99affd8bc37d20f4 726 725 2016-05-29T21:16:07Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = 5n+1 -1/4</math> '''Passo base:''' P(0) <math>1= {5^(0+1)}-1/ 4</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). P(0) é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k)\to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro; isto é, <math>1+5+5^2+5^3+...+ 5k = 5.k+1 -1/4</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = 5.k+2 -1/4</math> Para fazer isso, iniciamos com p(k) e adicionamos o próximo termo, <math>5.k+1</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = 5.k+1 -1/4</math> <math>+5.k+1 = 5.k+1 </math> <math>1+5^2+5^3+...+5k+5k+1 = 5k+1 -1 +4.5k+1 /4</math> <math>= (1+4)5k+1 -1 /4</math> <math>= 5.5k+1 -1/4</math> <math>= 5k+2 -1 /4</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k).p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: alternativamente, a prova de <math>p(k)/cdot p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5.k+1 = 5.k+1 -1 /4 + 5.k+1</math> <math> = 5k+1 -1 + 4.5k+1</math> <math>= (1+4).5k+1 -1/4</math> <math>=5.5k+1-1/4</math> <math>=5k+2-1/4</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 252dc0aa0faa296e301d4f976056d4e357bf68cd 727 726 2016-05-29T21:38:52Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' P(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>P(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = {5{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>P(k)<\math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>P(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> <math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 89b3574e73b1fda514de949936dbc8364392aaeb 728 727 2016-05-29T21:41:31Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' P(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>P(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = {5{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5.k+1 = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>P(k)<\math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>P(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 0c6a65e068058ece1c3bf250525c524eb35d3d83 729 728 2016-05-29T21:42:29Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' P(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>P(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = {5{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>P(k)<\math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>P(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 31df5663314978680035aaececa5276fce435d49 730 729 2016-05-29T21:45:33Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' P(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>P(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = {5{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>P(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>P(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>/sum limits{k=1}^n (2k+3)$= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1) afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k).p(k+1)</math>: suponha que p(k) é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. f2fa8f9a6fa0ba1bfa426aeff31f43056a1dd869 731 730 2016-05-29T21:59:10Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' P(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>P(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>P(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>P(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4) para todo n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 957c1c5537bab6a0625ffe499e703ffd6d6af916 732 731 2016-05-29T22:00:34Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja P(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' P(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>P(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to P(k+1)</math>: suponha para qualquer k, <math>P(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>P(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>P(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>P(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 8f02a6ac944ef2bc099da2555f535381158bb984 733 732 2016-05-29T22:06:29Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. e08854752623e04152356a4d2ef2e23eeca6d49a 734 733 2016-05-29T22:12:24Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>"(\frac 1 – {1}{2}^2)"</math> (1- 1/3²) (1 – ¼²)...(1 – 1/n²) Para n>=2, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando n= 2,3,4,5 obtemos: (1-1/2²) = ¾ (1-1/2² ) (1-1/3²)= 3/4 . 8/9 = 2/3 (1-1/2²) (1-1/3²) (1-1/4²) = 3/4 . 8/9 . 15/16 = 5/8 (1-1/2²) (1-1/3²) (1-1/4²) (1-1/5²) = 3/4 . 8/9 . 15/16 . 24/25 = 3/5 Assim, os produtos são 3/4, 2/3, 5/8, 3/5. Podemos reescrever essas frações como 3/4, 4/6, 5/8, 6/10. Isso sugere n+1/2n como a forma geral da soma. Seja p(n) : (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n Agora, tentamos mostrar que p(n) é verdadeiro para todo n>=2. Passo base: p(2) é verdadeiro. P(2) afirma que (1-1/2²) = 2+1/2.2, que é verdade pois, ambos os lados são iguais a ¾. Passo indutivo: P(k)p(k+1): suponha que p(k) é verdadeiro para algum k. portanto; (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/k²) = k+1/2k Multiplica-se ambos os lados da equação por (1-1/(k+1)²) para obter (1-1/2²) (1-1/3²) (1-1/4²) ...(1-1/k²) (1-1/(k+1)²) = k+1/2k (1-1/(k+1)²) = k+1/2k ((k+1)² - 1/ (k+1)²) = k+1/2k . k(k+2)/(k+1)² = k+2/2(k+1) Que é p(k+1). Portanto, pelo princípio da indução matemática, (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n é verdadeiro para todo n >=2. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 85047705ac211c28c756dd75d6805a54b2f614c4 735 734 2016-05-29T22:13:32Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(\frac 1 – {1}{2}^2)</math> (1- 1/3²) (1 – ¼²)...(1 – 1/n²) Para n>=2, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando n= 2,3,4,5 obtemos: (1-1/2²) = ¾ (1-1/2² ) (1-1/3²)= 3/4 . 8/9 = 2/3 (1-1/2²) (1-1/3²) (1-1/4²) = 3/4 . 8/9 . 15/16 = 5/8 (1-1/2²) (1-1/3²) (1-1/4²) (1-1/5²) = 3/4 . 8/9 . 15/16 . 24/25 = 3/5 Assim, os produtos são 3/4, 2/3, 5/8, 3/5. Podemos reescrever essas frações como 3/4, 4/6, 5/8, 6/10. Isso sugere n+1/2n como a forma geral da soma. Seja p(n) : (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n Agora, tentamos mostrar que p(n) é verdadeiro para todo n>=2. Passo base: p(2) é verdadeiro. P(2) afirma que (1-1/2²) = 2+1/2.2, que é verdade pois, ambos os lados são iguais a ¾. Passo indutivo: P(k)p(k+1): suponha que p(k) é verdadeiro para algum k. portanto; (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/k²) = k+1/2k Multiplica-se ambos os lados da equação por (1-1/(k+1)²) para obter (1-1/2²) (1-1/3²) (1-1/4²) ...(1-1/k²) (1-1/(k+1)²) = k+1/2k (1-1/(k+1)²) = k+1/2k ((k+1)² - 1/ (k+1)²) = k+1/2k . k(k+2)/(k+1)² = k+2/2(k+1) Que é p(k+1). Portanto, pelo princípio da indução matemática, (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n é verdadeiro para todo n >=2. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. d4f558a6e46c6b3cec525135074cfeff1b3e8175 736 735 2016-05-29T22:15:27Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 – \frac {1}{2}^2)</math> (1- 1/3²) (1 – ¼²)...(1 – 1/n²) Para n>=2, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando n= 2,3,4,5 obtemos: (1-1/2²) = ¾ (1-1/2² ) (1-1/3²)= 3/4 . 8/9 = 2/3 (1-1/2²) (1-1/3²) (1-1/4²) = 3/4 . 8/9 . 15/16 = 5/8 (1-1/2²) (1-1/3²) (1-1/4²) (1-1/5²) = 3/4 . 8/9 . 15/16 . 24/25 = 3/5 Assim, os produtos são 3/4, 2/3, 5/8, 3/5. Podemos reescrever essas frações como 3/4, 4/6, 5/8, 6/10. Isso sugere n+1/2n como a forma geral da soma. Seja p(n) : (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n Agora, tentamos mostrar que p(n) é verdadeiro para todo n>=2. Passo base: p(2) é verdadeiro. P(2) afirma que (1-1/2²) = 2+1/2.2, que é verdade pois, ambos os lados são iguais a ¾. Passo indutivo: P(k)p(k+1): suponha que p(k) é verdadeiro para algum k. portanto; (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/k²) = k+1/2k Multiplica-se ambos os lados da equação por (1-1/(k+1)²) para obter (1-1/2²) (1-1/3²) (1-1/4²) ...(1-1/k²) (1-1/(k+1)²) = k+1/2k (1-1/(k+1)²) = k+1/2k ((k+1)² - 1/ (k+1)²) = k+1/2k . k(k+2)/(k+1)² = k+2/2(k+1) Que é p(k+1). Portanto, pelo princípio da indução matemática, (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n é verdadeiro para todo n >=2. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 24311fa4d6779970f7653dcf4b87179b8573cf33 737 736 2016-05-29T22:25:03Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 – \frac {1}{2^2})</math> (1- 1/3²) (1 – ¼²)...(1 – 1/n²) Para n>=2, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando n= 2,3,4,5 obtemos: (1-1/2²) = ¾ (1-1/2² ) (1-1/3²)= 3/4 . 8/9 = 2/3 (1-1/2²) (1-1/3²) (1-1/4²) = 3/4 . 8/9 . 15/16 = 5/8 (1-1/2²) (1-1/3²) (1-1/4²) (1-1/5²) = 3/4 . 8/9 . 15/16 . 24/25 = 3/5 Assim, os produtos são 3/4, 2/3, 5/8, 3/5. Podemos reescrever essas frações como 3/4, 4/6, 5/8, 6/10. Isso sugere n+1/2n como a forma geral da soma. Seja p(n) : (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n Agora, tentamos mostrar que p(n) é verdadeiro para todo n>=2. Passo base: p(2) é verdadeiro. P(2) afirma que (1-1/2²) = 2+1/2.2, que é verdade pois, ambos os lados são iguais a ¾. Passo indutivo: P(k)p(k+1): suponha que p(k) é verdadeiro para algum k. portanto; (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/k²) = k+1/2k Multiplica-se ambos os lados da equação por (1-1/(k+1)²) para obter (1-1/2²) (1-1/3²) (1-1/4²) ...(1-1/k²) (1-1/(k+1)²) = k+1/2k (1-1/(k+1)²) = k+1/2k ((k+1)² - 1/ (k+1)²) = k+1/2k . k(k+2)/(k+1)² = k+2/2(k+1) Que é p(k+1). Portanto, pelo princípio da indução matemática, (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = n+1/2n é verdadeiro para todo n >=2. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. a41022f2e8a871110acbc215f1f23683d2140ddb 738 737 2016-05-29T22:55:45Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 – \frac {1}{2^2})</math> (1- 1/3²) (1 – ¼²)...(1 – 1/n²) Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: (1-1/2²) = <math>\frac {3}{4}</math> (1-1/2² ) (1-1/3²)= <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> (1-1/2²) (1-1/3²) (1-1/4²) = <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> (1-1/2²) (1-1/3²) (1-1/4²) (1-1/5²) = <math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}<\math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja p(n) : (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. portanto; (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/k²) = <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por (1-1/(k+1)²) para obter (1-1/2²) (1-1/3²) (1-1/4²) ...(1-1/k²) (1-1/(k+1)²) = <math>\frac {k+1}{2k} (1-1/(k+1)²)</math> <math>=\frac {k+1}{2k} ((k+1)² - 1/ (k+1)²)</math> <math>=\frac {k+1}{2k} . k(k+2)/(k+1)²</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. 0e974131cf4b8e78a4a761a21e07dcfeea2976d1 739 738 2016-05-29T23:01:38Z Gellyviana 31 /* Referências */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 – \frac {1}{2^2})</math> (1- 1/3²) (1 – ¼²)...(1 – 1/n²) Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: (1-1/2²) = <math>\frac {3}{4}</math> (1-1/2² ) (1-1/3²)= <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> (1-1/2²) (1-1/3²) (1-1/4²) = <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> (1-1/2²) (1-1/3²) (1-1/4²) (1-1/5²) = <math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}<\math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja p(n) : (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. portanto; (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/k²) = <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por (1-1/(k+1)²) para obter (1-1/2²) (1-1/3²) (1-1/4²) ...(1-1/k²) (1-1/(k+1)²) = <math>\frac {k+1}{2k} (1-1/(k+1)²)</math> <math>=\frac {k+1}{2k} ((k+1)² - 1/ (k+1)²)</math> <math>=\frac {k+1}{2k} . k(k+2)/(k+1)²</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, (1-1/2²) (1-1/3²) (1-1/4²)...(1-1/n²) = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. e6db6139174889f4145f7c69e4736dc0c9fd8c4d 741 739 2016-05-30T00:17:11Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})<\math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) = <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}<\math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. portanto; <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 – k – 1 = (k^² - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>.<math> *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1<math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 0a55546fb96bb82bb33ee08a8c1f1a51df6a3e27 742 741 2016-05-30T00:18:24Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) = <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}<\math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. portanto; <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 – k – 1 = (k^² - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>.<math> *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1<math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 91f85c73cea3a1f65dc06b0207999cf22156ab12 743 742 2016-05-30T00:21:02Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) = <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. portanto; <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 – k – 1 = (k^² - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>.<math> *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1<math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 8931fc8f7a4edacb50b64ed8764dfb21c295c039 744 743 2016-05-30T00:25:01Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) = <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. portanto; <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1<math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 749b58a9fb663bb4ea76f0fe76bdb5d3ff9e605b 745 744 2016-05-30T00:30:16Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1<math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 930bb577c52f638e8f882a8bfe6abd7d4f4a0a55 752 745 2016-05-30T00:39:05Z Gellyviana 31 /* Implementações em C++(Exemplos 1, 2, 3 e 4) */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1<math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 3c59524a9476c8e40f0904536ec1c36cb046083e 753 752 2016-05-30T00:59:55Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> https://www.overleaf.com/read/cfxwfcrdhmwc Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 3dea6c9ea4442c1331e82102392551b30e183fad 754 753 2016-05-30T01:03:01Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. dd2ae6064838eb45b0f561e6914f48aaa26efe84 Árvores 0 184 714 711 2016-05-29T20:11:40Z Paulohq 21 /* Computações e Explorações */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == Mostre todas as árvores com seis vértices. Solução Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. Solução Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. Solução Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. Solução Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. Solução Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> 2bf1686c165af6490ec67216bdba962444389809 716 714 2016-05-29T20:17:44Z Paulohq 21 /* Computações e Explorações */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução à Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> 08b9ee0360b76d7af067d3377d3774d06d806f6a 717 716 2016-05-29T20:19:18Z Paulohq 21 /* Introdução à Árvores */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvore == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> a7e5ae262edf5fcbb1a3fb91fcfd4da12fa4d999 718 717 2016-05-29T20:20:34Z Paulohq 21 /* Percursos em Árvore */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> 179a8897152b8251cc3c3e74c1a8753a93e7c828 719 718 2016-05-29T20:29:12Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Referências == 67c67540e430dd9d4c1bcede8ef3ce74f020e115 720 719 2016-05-29T20:43:06Z Paulohq 21 /* Referências */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. cc27bb3ec0736d242ce5f484c3841d91547a5d7e 740 720 2016-05-29T23:49:31Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. b08e8b8cee4e59155281b643745881f6f51d3c3f 751 740 2016-05-30T00:34:22Z Paulohq 21 /* Exercícios e Projetos */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivechio, da turma XX da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. 1. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. b0c1f5b6097276157e03fe93bde7dc2abd4c6f26 755 751 2016-05-30T03:35:52Z Paulohq 21 /* Exercícios e Projetos */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivechio, da turma XX da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 9216728d11a65b756a85d3ff6084ce71f6c9e8ef 756 755 2016-05-30T03:36:14Z Paulohq 21 /* Exercícios e Projetos */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivechio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 37dd2e19bdbcde26cf73f65a9d14e64b59e6f83b 757 756 2016-05-30T03:37:29Z Paulohq 21 /* Exercícios e Projetos */ wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são conectados grafos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não produzir uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores.Nós iremos demonstrar como resolver vários problemas, onde árvores fazem um papel importante usando Maple, como procurando e construindo códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Nós iremos descrever como usar o Maple para fazer diferentes métodos de percorrer árvore, onde o percurso é a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar spanning trees de grafos. Então, nós iremos mostrar como usar o Maple como resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar spanning trees de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 8c66952b5d1f6c47771794821676b2be205e4aee 758 757 2016-05-30T12:20:47Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa algumas capacidades embutidas do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a simples ciclos, quando o texto se refere a simples circuitos. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando um nó desejado para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como 1st, 2nd,...,m-th filhos se houverem m filhos de um dado vértice. Nós iremos fazer uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples em 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1.O grafo é conectado. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comandoscomponents, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo, e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G por si só, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 98778bb5033bb8cac04459f60504286d17460943 759 758 2016-05-30T12:30:27Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos particulares são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para mudar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para refletir a estrutura da spanning tree. Para usar o comando spantree, devemos selecionar o vértice e formar uma spanning tree com esse vértice como raiz, direcionando todas as arestas na árvore em direção a raiz. (Estudaremos spanning trees mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma spanning tree de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora representamos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até não haverem mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até não haverem mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure é como a seguinte: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore mais larga, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descentendes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex:.uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com gráu de vértice 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceado. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1 se uma árvore tem um total de h níveis, onde o nível do vértice é a distância do caminho único from da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequencia de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore em Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para esse vértice. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Esaa técnica é formalizade pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvore m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se não houverem filhos do vértice, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores de busca binárias. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores em código Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a binary estrutura binária da árvore para tomar decisões binárias(ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 57128fb9c5ca1b5e73474131c75417aa38838b30 761 759 2016-05-30T12:53:24Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Click here to access a summary of all the Maple code used in this section. Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Click here to access a summary of all the Maple code used in this section. Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. b3b9894f64b333687c2b4ce988398ed8b7157930 762 761 2016-05-30T12:54:09Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho a esquerda ou o filho a direita, onde o filho a esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de buscar exigido to para encontrar um específico elemento da árvore é logarítmico no número de vértices da árvore. A maior desvantegem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes em mais detalhe enquanto percorremos a própria impementação de um algoritmo de inserção binária. Requerimos marcações no vértice. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice tipico na árvore tem dois descendentes (filhas). Devemos ser capazes de especificar quais desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação em outro ponto posterior, refazendo o código do algoritmo inteiro. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que a computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenadapor inserção é como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando fazemos comparação. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. se v =cur_vertex, está feito. 4. se v <cur_vertex então procure o filho a esquerda, caso contrário, procure o filho a direita. Isso é feito mudando cur_vertex para ser o filho a esquerda ou a direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes e x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo filho. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione em x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui em uma maneira que atualiza os pesos de cada vértice na medida que ele é criado. Isso serve para indicar se é vértice é um descendente a esquerda ou a direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que qaiquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada a qual é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem procuradas. O seguinte procedure BiSearch faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. d34fbe1832e1fc140bedc69614579c2a3f0c2814 File:Captura de tela de 2016-05-29 20-22-52.png 6 185 746 2016-05-30T00:32:14Z Gellyviana 31 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Captura de tela de 2016-05-29 20-23-27.png 6 186 747 2016-05-30T00:32:46Z Gellyviana 31 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Captura de tela de 2016-05-29 20-24-41.png 6 187 748 2016-05-30T00:33:11Z Gellyviana 31 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Captura de tela de 2016-05-29 20-25-21.png 6 188 749 2016-05-30T00:33:32Z Gellyviana 31 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Captura de tela de 2016-05-29 20-25-36.png 6 189 750 2016-05-30T00:33:53Z Gellyviana 31 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Fundamentos Matemáticos da Computação 2 0 125 760 696 2016-05-30T12:38:56Z Clah 19 wikitext text/x-wiki [[Álgebra Booleana]] [[Árvores]] [[Indução e Recursão Matemática]] [[Relações]] 302a5db26fcd9f9e041805bd3e0ef7c2e07e9cd5 Árvores 0 184 763 762 2016-05-30T13:19:29Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base test nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudo-código: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Ele é baseado num algoritmo ganancioso, onde em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições óptimas. O seguinte pseudo-código descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudo-código, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudo-código acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudo-código, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudo-código: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudo-código é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudo-código. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudo-código, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudo-código: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudo-código para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudo-código é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudo-código do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudo-código para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudo-código no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 3b42b121cd379065995112f7a1873c3a1a8f11f1 765 763 2016-05-30T13:30:42Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, onde a ordenação é com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho a esquerda e y como o filho a direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por examplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres em inglês emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número que desse vértice em termos de seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que em fato, a ordenação por baixo é simplesmente alfabético nos nomes dos vértices. Primeiro implementamos o algaritmo de percurso em pré-ordem. Esse visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Ponha o atua vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, seguida pela sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 59537aa86e625a82036913c79063eca3b62f3c26 766 765 2016-05-30T13:40:57Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma comparação de rotina diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, enraizado no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais a esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice, r então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais a esquerda(enraizada no filho mais a esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determina a ordem dos filhos e percorra a sub-árvore baseada no filho mais a esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma comparação de rotina diferentes para ordenar os descendentes. Teste esse novo procedure executando ele em tree Trav. <pre> Inorder(Trav, a); </pre> O percurso final que devemos implementar é em pós-ordem. Percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na tree Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas estejam enraizadas no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. df07083c4db92c5a48c4be9d0cffe9df830ac509 767 766 2016-05-30T14:28:38Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma rotina de comparação diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, com raiz no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais à esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice r, então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais à esquerda (com raiz no filho mais á esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determine a ordem dos filhos e percorra a sub-árvore baseada no filho mais à esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma rotina de comparação diferente para ordenar os descendentes. Teste esse novo procedure executando ele na árvore Trav. <pre> Inorder(Trav, a); </pre> O percurso final que implementaremos é em pós-ordem. O percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que apenas visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na árvore Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, como usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em um representação de árvore binária. Essa representação é árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementear, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y as [x,"+",y] onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente encodadas da string + que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando a esquerda, um operador e um operando a direita. Use o algoritmo para construir a árvore binária para operando a esquerda. 3. Repita o passo (2) no operando a direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é a seguinte: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputando a raiz da árvore é estranho e caro, então nós simplesmente pedimos a cada árvore que se lembre sua própria raiz. Isso feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma expressão árvore para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma dada expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente a lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas tenham raiz no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 656fffe7ee24e8dd13af9eef700c211895d9fd95 768 767 2016-05-30T14:57:40Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma rotina de comparação diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, com raiz no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais à esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice r, então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais à esquerda (com raiz no filho mais á esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determine a ordem dos filhos e percorra a sub-árvore baseada no filho mais à esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma rotina de comparação diferente para ordenar os descendentes. Teste esse novo procedure executando ele na árvore Trav. <pre> Inorder(Trav, a); </pre> O percurso final que implementaremos é em pós-ordem. O percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que apenas visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na árvore Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e também como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em uma representação de árvore binária. Essa representação em árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementar, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y como [x,"+",y], onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente codificadas da string +, que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário para isso em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando à esquerda, um operador, e um operando à direita. Use o algoritmo para construir a árvore binária para o operando à esquerda. 3. Repita o passo (2) no operando à direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é esta: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputar a raiz da árvore é complicado e taxativo, então nós simplesmente pedimos a cada árvore que se lembre de sua própria raiz. Isso é feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma árvore de expressão para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. Cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente à lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Nós também iremos usar Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa é de dividir a lista em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela é ordenada em ordem, então retornamos L como ela é. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 10000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 1000 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Tome nota especial para a importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas tenham raiz no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 427dc83b27ec377e2013e4c0682c90fde90ef86d 769 768 2016-05-30T16:25:42Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma rotina de comparação diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, com raiz no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais à esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice r, então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais à esquerda (com raiz no filho mais á esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determine a ordem dos filhos e percorra a sub-árvore baseada no filho mais à esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma rotina de comparação diferente para ordenar os descendentes. Teste esse novo procedure executando ele na árvore Trav. <pre> Inorder(Trav, a); </pre> O percurso final que implementaremos é em pós-ordem. O percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que apenas visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na árvore Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e também como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em uma representação de árvore binária. Essa representação em árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementar, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y como [x,"+",y], onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente codificadas da string +, que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário para isso em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando à esquerda, um operador, e um operando à direita. Use o algoritmo para construir a árvore binária para o operando à esquerda. 3. Repita o passo (2) no operando à direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é esta: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputar a raiz da árvore é complicado e taxativo, então nós simplesmente pedimos a cada árvore que se lembre de sua própria raiz. Isso é feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma árvore de expressão para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. Cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente à lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Usaremos também o Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa lista é de dividi-la em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela está ordenada, então retornamos L. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 1000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 100 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Atente-se à importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usar árvores de extensão para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar as árvores de extensão usando dois algoritmos: depth-first search e breadth-first search. Então nós iremos mostrar como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search usada para resolver vários problemas. Para começar, nós iremos discutir como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo (busca em profundidade, em português) sugere, os vértices são visitados em ordem de crescimento de profundidade da árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e conjunto w igual a x. 3. Repita o passo (2) até não haver mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Nós demonstramos o uso do procedure de depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. Especificamente, o algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo do algoritmo. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V até cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas tenham raiz no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 617ee52a513b6fbea470e74ffd99953f4270fea1 770 769 2016-05-30T16:41:27Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma rotina de comparação diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, com raiz no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais à esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice r, então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais à esquerda (com raiz no filho mais á esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determine a ordem dos filhos e percorra a sub-árvore baseada no filho mais à esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma rotina de comparação diferente para ordenar os descendentes. Teste esse novo procedure executando ele na árvore Trav. <pre> Inorder(Trav, a); </pre> O percurso final que implementaremos é em pós-ordem. O percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que apenas visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na árvore Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e também como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em uma representação de árvore binária. Essa representação em árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementar, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y como [x,"+",y], onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente codificadas da string +, que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário para isso em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando à esquerda, um operador, e um operando à direita. Use o algoritmo para construir a árvore binária para o operando à esquerda. 3. Repita o passo (2) no operando à direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é esta: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputar a raiz da árvore é complicado e taxativo, então nós simplesmente pedimos a cada árvore que se lembre de sua própria raiz. Isso é feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma árvore de expressão para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. Cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente à lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Usaremos também o Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa lista é de dividi-la em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela está ordenada, então retornamos L. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 1000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 100 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Atente-se à importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usá-las para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar essas árvores usando dois algoritmos: depth-first search (busca em profundidade) e breadth-first search (busca em largura). Então, mostraremos como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search, usada para resolver vários problemas. Para começar, discutiremos como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo sugere, os vértices são visitados em ordem crescente de profundidade na árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e faça conjunto w = x. 3. Repita o passo (2) até não ter mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Demonstramos o uso do procedure depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. O algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo dele. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V para cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas tenham raiz no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema com uma solução elegante de backtracking é o problema de posicionar n-rainhas em um tabuleiro de xadrez nXn de maneira que nenhuma rainha tem como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionadona mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro de uma maneira gananciosa, até que todas as rainhas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. 23dc8efe3ee2ff3629bd8187e0bed7b2ca79e836 782 770 2016-06-01T17:24:40Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma rotina de comparação diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, com raiz no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais à esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice r, então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais à esquerda (com raiz no filho mais á esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determine a ordem dos filhos e percorra a sub-árvore baseada no filho mais à esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma rotina de comparação diferente para ordenar os descendentes. Teste esse novo procedure executando ele na árvore Trav. <pre> Inorder(Trav, a); </pre> O percurso final que implementaremos é em pós-ordem. O percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que apenas visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na árvore Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e também como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em uma representação de árvore binária. Essa representação em árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementar, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y como [x,"+",y], onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente codificadas da string +, que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário para isso em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando à esquerda, um operador, e um operando à direita. Use o algoritmo para construir a árvore binária para o operando à esquerda. 3. Repita o passo (2) no operando à direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é esta: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputar a raiz da árvore é complicado e taxativo, então nós simplesmente pedimos a cada árvore que se lembre de sua própria raiz. Isso é feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma árvore de expressão para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. Cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente à lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Usaremos também o Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa lista é de dividi-la em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela está ordenada, então retornamos L. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 1000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 100 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Atente-se à importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usá-las para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar essas árvores usando dois algoritmos: depth-first search (busca em profundidade) e breadth-first search (busca em largura). Então, mostraremos como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search, usada para resolver vários problemas. Para começar, discutiremos como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo sugere, os vértices são visitados em ordem crescente de profundidade na árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e faça conjunto w = x. 3. Repita o passo (2) até não ter mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Demonstramos o uso do procedure depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. O algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo dele. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V para cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas tenham raiz no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para achar soluções para problemas que poderiam ser impráticos de se resolver usando técnicas de busca excessiva, O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos comos usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas de posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que consiste em encontrar um subconjunto de um conjunto de inteiros cuja soma é dado inteiro. O primeiro problemas nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, onde n é um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema que pode ser solucionado com backtracking é o de posicionar n-rainhas em um tabuleiro de xadrez de dimensão nXn de maneira que nenhuma rainha tenha como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionado na mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro até que todas elas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. f1becf2bd2c15e8b8070524ee243803b0d024aab 783 782 2016-06-01T18:04:49Z Paulohq 21 wikitext text/x-wiki Esse capítulo é dedicado aos aspectos computacionais do estudo das árvores. Árvores são um tipo específico de grafo, que são grafos conexos simples que não tem circuitos simples. O código Maple nesse capítulo assume que você está usando uma versão atualizada do Maple Network Package. Essas melhorias afetam principalmente a exibição das árvores. Em particular, o comando draw foi atualizado para se entender como desenhar árvores com raiz. Para testar se você está utilizando a versão correta, carregue o pacote networks e rode a versão comando, como em: <pre>with(networks): version();</pre> Se esse comando não retornar uma descrição da versão, então vocês está utilizando a versão errada. Uma versão apropriada pode ser encontrada no site ftp: http://www.mhhe.com/math/advmath/rosen/r5/instructor/maple.html junto com instruções de instalação. Primeiro, nós iremos discutir como representar, desenhar, e trabalhar com árvores usando o Maple. Especificamente, nós iremos descrever como representar e construir árvores e derivar características básicas sobre árvores em Maple. Nós iremos demonstrar como utilizar o Maple para desenhar árvores. Iremos também demonstrar como resolver vários problemas, onde árvores desempenham um papel importante usando Maple, como procurar e construir códigos prefixos, usando uma implementação específica do algoritmo de Huffman. Vamos descrever como usar o Maple para fazer diferentes métodos de percorrer uma árvore, sendo o percurso a visita dos vértices da árvore em uma ordem pré-definida. Então nós iremos discutir como esses percursos se relacionam com o tópico de organização. Continuamos mostrando como usar o Maple para criar árvores de extensão de grafos. Então, nós iremos mostrar como usar o Maple para resolver vários problemas utilizando backtracking. Finalmente, iremos mostrar como encontrar árvores de extensão de peso mínimo de grafos ponderados usando Maple. ==Introdução às Árvores== Para começar, iremos demonstrar como construir árvores em Maple. Dada uma árvore sem raiz, nós podemos construir essa árvore em Maple assim como faríamos com qualquer grafo. Nós também iremos dar um procedure que usa alguns comandos do Maple que determinam se um grafo específico é uma árvore. Antes de entrar na implementação, há dois pontos importantes que devem ser mencionados. Primeiro, notamos que o Maple difere da terminologia de texto, no senso que o Maple refere-se a ciclos simples, enquanto o texto se refere a circuitos simples. O segundo ponto que vale mencionar é de que uma árvore sem raiz é um grafo simples que não tem ciclos simples. Estruturalmente falando, uma árvore com raiz é exatamente a mesma coisa que uma árvore sem raiz, com a propriedade adicional de que há um vértice específico chamado de raiz, que é visto como o ponto inicial de uma árvore. Em termos de implementação Maple, representamos árvores sem raiz como grafos, e criamos árvores sem raiz a partir de árvores sem raiz utilizando comandos Maple como spantree, que serão abordados posteriormente, especificando uma raiz desejada para uma árvore sem nó. Outro tipo importante de árvore é a árvore ordenada, que é uma árvore com raiz onde os filhos de um vértice ordenados de alguma maneira como primeiro, segundo,...,n-ésimo filhos se houverem n filhos de um dado vértice. Faremos uso do peso dos vértices para determinar a ordem dos filhos de um vértice específico. Esse tipo de árvore irá aparecer posteriormente, mas é importante distinguir árvores sem raiz, com raiz e desordenadas, e árvores com raiz e ordenadas. Como primeiro exemplo, iremos discutir árvores sem raiz. Criamos uma árvore exatamente da mesma maneira que criamos um grafo, usando o pacote networks do Maple. Como nosso primeiro exemplo, iremos criar uma árvore simples com 4 vértices. <pre> with(networks): new(T1): addvertex(a,b,c,d,f,g,T1): addedge(a,b,a,c,a,d,b,f,b,g, T1): draw(Tree(a),T1); </pre> Suponha que fomos dados um grafo e se foi pedido para determinar se ele é ou não uma árvore. Pela definição de árvores, precisamos verificar as 3 seguintes propriedades: 1. O grafo é conexo. 2. O grafo é simples. 3. O grafo não tem ciclos. Usando Maple, essas propriedades são facilmente verificadas. Em particular, podemos determinar se um grafo é conectado em Maple usando o comando components, que retorna uma coleção de conjuntos de vértices, onde cada conjunto nessa coleção contém os vértices de um componente conexo do grafo. Podemos determinar se um grafo é simples usando o comando Maple gsimp, que retorna a árvore simples por baixo de um multigrafo (ou pseudografo), e então comparando o número de arestas da árvore por baixo do grafo original. Isso nos leva até o procedure IsSimple. <pre> IsSimple := proc(G::graph) local H; H := networks[duplicate](G); if nops(edges(gsimp(H))) = nops(edges(G)) then true else false fi; end: </pre> Note que não devemos simplificar G, pois tal simplificação é um processo irreversível. Para testar conectividade, utilizamos o procedure IsConnected <pre> IsConnected := proc(G::graph) evalb(nops(components(G)) = 1) end: </pre> Podemos determinar se um grafo tem ou não ciclos utilizando o comando cyclebase do Maple que retorna um conjunto de ciclos, ou circuitos simples, que formam uma base de todos os ciclos (circuitos simples) no grafo dado; se o cyclebase não tiver ciclos, o grafo não tem ciclos. Isso, junto com os dois testes anteriores pode ser usado para testar se um grafo é uma árvore. <pre> IsTree:=proc(G::graph) if not (IsConnected(G) and IsSimple(G)) then RETURN(false); fi; if cyclebase(G) = {} then RETURN(true); else RETURN(false); fi; end: </pre> Se você preferir, pode substituir o teste cycle base nesse procedure por um que verifica se o número de arestas é um a menos que o número de vértices. Agora estamos prontos para usar o procedure IsTree para determinar se alguns grafos são árvores; <pre> IsTree(T1); IsTree(complete(3)); </pre> === Árvores com raiz === Até esse ponto, nós lidamos apenas com árvores sem raiz. Podemos usar o comando Maplespantree para transformar uma árvore sem raiz em uma árvore com raiz. Ele faz isso atualizando os conjuntos de ancestrais e filhas (descendentes) para cada vértice, para imitar a estrutura da árvore de extensão. Para usar o comando spantree, devemos selecionar um vértice e formar uma árvore de extensão usando esse vértice como raiz, direcionando todas as arestas da árvore em direção a raiz. (Estudaremos árvores de extensão mais tarde nesse capítulo. Geralmente, o comando spantree pega um grafo conexo indireto G e um vértice v e constrói uma árvore de extensão de G usando v como a raiz, direcionando todas as arestas em direção a v.) Por exemplo, podemos transformar a árvore T1 em uma árvore com raiz, tomando a como sua raiz e utilizando o comando: <pre> T2:=spantree(T1, a): </pre> Podemos facilmente observar relações entre vértices de uma árvore utilizando comandos embutidos no Maple. Entre os comandos que são úteis para isso estão daughter, ancestor, neighbours e departures. O comando daughter encontra os filhos de um vértice em uma árvore com raiz, e o comando ancestor do Maple encontra o vértice pai de um vértice em uma árvore com raiz. Os comandos neighbors e departures agem de maneira similar, determinando os filhos de um vértice em uma árvore com raiz. Para ilustrar o uso de alguns desses comandos no Maple, podemos determinar relações de árvores como pais, filhos, ancestrais e descendentes de vértices específicos. Por exemplo, podemos encontrar os filhos do vértice a na árvore T2, usando o comando: <pre> daughter(a, T2); </pre> Para achar o pai de d na árvore T2, usamos o comando: <pre> ancestor(d, T2); </pre> Agora apresentaremos um procedure que encontra todos os descendentes, ancestrais e irmãos de um vértice particular em uma árvore com raiz. Esse procedure, chamado Family, pode ser descrito usando o seguinte pseudocódigo: 1. Para encontrar todos os ancestrais, usamos o comando ancestor do Maple até que não hajam mais ancestrais (ex: quando atingimos o vértice raiz). 2. Para achar todos os descendentes, usamos o comando daughter repetidamente até que não hajam mais descendentes(ex: quando todas as folhas de um vértice forem atingidas). 3. Para se achar todos os irmãos de um vértice v, primeiros encontramos o ancestral de v, chamado w; os irmãos de v são descendentes de outro w de v. Uma implementação desse procedure se dá da seguinte maneira: <pre> Family := proc(v::name,G::graph) local Temp, Ancestors, Descendants, Siblings; Ancestors := ancestor(v,G); Temp := ancestor(v,G); while not (Temp = {}) do Ancestors := Ancestors union Temp; Temp := ancestor(Ancestors,G); od; Descendants := daughter(v,G); Temp := daughter(v,G); while not (Temp = {}) do Descendants := Descendants union Temp; Temp := daughter(Descendants,G); od; Siblings := daughter(ancestor(v, G), G) minus v; [Ancestors,Siblings,Descendants]; end: </pre> Agora iremos construir uma árvore maior, chamada T3 que é a árvore mostrada na Página 5433 do texto, e então iremos executar o novo procedure criado em um de seus vértices. <pre> new(T3): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M,N,T3): addedge( [A,B],[A,J],[A,K],[B,C],[B,E],[B,F], [C,D],[F,G],[F,I],[G,H],[K,L],[L,M],[L,N], T3): draw(Tree(A),T3); </pre> Os descendentes do vértice B são obtidos pelo comando: <pre> Bfamily := Family(B,T3); Bfamily[3]; </pre> A seguir, determinamos o conjunto de vértices internos (galhos) e folhas de uma árvore com raiz. Lembre-se que um v é um vértice interno de uma árvore com raiz se v tiver filhos, e que v é o vértice folha de uma árvore com raiz se v não tiver filhos. Em outras palavras, em qualquer árvore com raiz não trivial (ex: uma árvore com raiz que é mais do que apenas um vértice raiz), as folhas são essas com grau 1, e os vértices internos são vértices com grau maior que 1. Sabendo disso, podemos usar o comando Maplevdegree para determinar o conjunto de folhas e o conjunto de vértices internos dada uma árvore com raiz. <pre> Leaves:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) < 2 ) end, vertices(T) minus root , T ); end: Internal:=proc(T::graph, root::name) select( proc(x,T) evalb( vdegree(x,T) > 1 ) end, vertices(T) minus root , T ); end: Leaves(T2, a); Internal(T2,a); </pre> Agora iremos discutir como encontrar o maior número de filhos de um vértice interno de uma árvore com raiz. Lembre-se que se m é esse número, a árvore é chamada de árvore m-ária. Também iremos descrever como determinar se uma árvore m-ária é balanceada. Lembre-se que uma árvore é balanceada se todas as folhas estão no nível h ou h-1, sendo essa uma árvore com um total de h níveis, onde o nível do vértice é a distância do caminho único da raiz até tal vértice. Para utilizar o Maple para determinar se uma árvore é uma árvore m-ária, podemos simplesmente olhar a sequência de graus do vértice, tomando em conta que para todos os vértices exceto a raiz, o grau de tal vértice é um a mais que o número de descendentes. Isso pode ser feito usando o comando vdegree no Maple. Para determinar se uma árvore é balanceada, podemos usar a estrutura de armazenamento interno de uma árvore no Maple. Iremos utilizar do fato de que o Maple armazena o nível do vértice em uma árvore como o peso do vértice para ele mesmo. Por exemplo, se v é um vértice que está no nível 3 de uma árvore, então podemos extrair essa informação usando o comando vweight no vértice v. Essa técnica é formalizada pelo seguinte procedure no Maple: <pre> ArityBalanced:=proc(G::graph, Root::name) local Leaf_Depth, V, Max_Children, is_balanced,i; V:=vertices(G); Leaf_Depth:={}; is_balanced:=false; for v in V do if (not (v = Root)) and (vdegree(v,G)=1) then Leaf_Depth:=Leaf_Depth union vweight(v, G); fi; od; if nops(Leaf_Depth) > 2 then printf(`The tree is not balanced`); elif nops(Leaf_Depth) = 1 then printf(`The tree is balanced`); is_balanced:=true; elif nops(Leaf_Depth) = 2 and abs(Leaf_Depth[1] - Leaf_Depth[2]) > 1 then printf(`The tree is not balanced`); else printf(`The tree is balanced %a`, Leaf_Depth ); is_balanced:=true; fi; Max_Children:=maxdegree(G)-1; if vdegree(Root, G) > Max_Children then Max_Children:=vdegree(Root, G); fi; printf(`The arity of the tree is %d`, Max_Children); [Max_Children, is_balanced]; end: </pre> <pre> ArityBalanced(T3, A): </pre> Agora iremos utilizar o procedure ArityBalanced para verificar a fórmula na página 541 do texto para árvores m-árias cheias. Isto é, iremos construir um procedure para computar o número de vértices internos e folhas de dada árvore m-ária, e comparar essas quantidades como esboçado no teorema 3 e teorema 4 da página 541 do texto. O procedure chamado TheoremVerify utilizará: <pre> TheoremVerify:=proc(G::graph, Root::name) local internal, m, leaves, n, i, V, is_full_tree; V:=vertices(G); n:=nops(V); i:=0; internal:=0; leaves:=0; is_full_tree:=true; </pre> Use o procedure ArityBalanced para determinar o número de argumentos <pre> m:=ArityBalanced(G, Root)[1]; while is_full_tree and i<n do i:=i+1; </pre> Se o vértice não tiver filhos, ele é uma folha <pre> if nops(daughter(V[i], G)) = 0 then leaves:=leaves+1; </pre> Se o número de filhos não for m, então não é uma árvore completa <pre> elif not (nops(daughter(V[i],G)) = m) then printf(`The tree is not a full tree`); is_full_tree:=false; </pre> O vértice atual é um vértice interno <pre> else internal:=internal+1; fi; od; if is_full_tree then printf(`Vertices count is %d`, n); printf(`Computed count (m*i+1) is %d`, m*internal + 1); printf(`Leaf count is %d`, leaves); printf(`Computed count ((m-1)*i + 1) is %d`, (m-1)*internal+1); fi; NULL; end: </pre> Utilizaremos o procedure TheoremVerify para verificar os teoremas 3 e 4 do texto em uma árvore 3-ária (ternária) completa. <pre> new(Full1): addvertex(A,2,3,4,5,6,7,8,9,10, Full1): addedge(A,2, A,3, A,4, 2,5, 2, 6, 2,7, 4,8, 4,9, 4,10, Full1): </pre> <pre> TheoremVerify(Full1, A); </pre> == Aplicação de Árvores == Essa sessão foca no uso de árvores em árvores binárias de busca. Especificamente, chamamos o uso de árvores em algoritmos de busca binária assim como o uso de árvores no algoritmo de Huffman. A razão pela qual desejamos usar árvores binárias é de que podemos usar a estrutura binária da árvore para tomar decisões binárias (ex: true/false) quanto a inserção ou procura de caminhos. Uma árvore é chamada de árvore binária se todos os vértices na árvore tiverem no máximo dois filhos. Nesse capítulo, iremos utilizar árvores binárias ordenadas. A ordenação dos vértices é simplesmente a marcação dos filhos de um vértice como o filho à esquerda ou filho à direita, onde o filho à esquerda é considerado o filho que deve ser visitado primeiro, e o da direita, o que deve ser visitado em segundo. === Inserção binária === Um benefício crucial em árvores binárias ordenadas é de que o tempo de busca exigido para encontrar um específico elemento da árvore é logarítmico ao número de vértices da árvore. A maior desvantagem é de que a inserção de um vértice é muito mais taxativa. Discutiremos estes aspectos em mais detalhe enquanto percorremos a própria implementação de um algoritmo de inserção binária. Os vértices devem se marcados. No Maple podemos utilizar o nome do vértice como marcação já que ele pode ser tanto inteiro como string. Um vértice na árvore tipicamente tem dois descendentes (filhas). Devemos ser capazes de especificar qual desses dois vértices é o descendente da esquerda e qual é o da direita. Podemos indicar isso utilizando o peso do vértice. No Maple, cada vértice tem um peso padrão de 0, como demonstrado no simples exemplo: <pre> new(g): addvertex(1,2,g): vweight(1,g); </pre> Podemos utilizar o peso do vértice para especificar uma ordenação da esquerda para a direita. Uma solução ainda mais simples é de simplesmente concordar que o peso do vértice é seu nome e impor uma ordenação nesses nomes. Para comparar o nome de dois vértices, podemos utilizar um procedure como: <pre> IsLessThan := proc(a,b) local t; if type( [a,b], [string,string]) then t := sort( [a,b] , lexorder ); else t := sort([a,b]); fi; if a = t[1] then true else false fi; end: </pre> Usar essa comparação nos permite geralmente ignorar que tipo de marcação está sendo utilizada. <pre> IsLessThan(1,2); IsLessThan(b,a); IsLessThan(1,b); </pre> Ela também facilita a mudança do critério de comparação caso seja necessário mais tarde, sem ter que refazer totalmente o código do algoritmo. Também precisaremos ser capazes de achar a raiz da árvore. O seguinte procedure calcula tal raiz e força a árvore a lembrar sua raiz, para que sua computação não precise ser repetida. <pre> FindRoot := proc(T::GRAPH) local v, V; V := vertices(T); if not assigned( T(Root) ) then for v in V do if indegree(v,T) = 0 then T(Root) := v; # remember the root fi; od; if not assigned( T(Root) ) then ERROR(`no root`) fi; fi; T(Root); end: </pre> O procedure para construir uma árvore binária ordenada por inserção acontece como o exemplo seguinte. Para facilitar, utilizamos o nome do vértice como seu valor quando estivermos comparando. 1. Dado um vértice v para inserir na árvore T, precisamos localizar o local correto na árvore T para inserir v. 2. Se a árvore T estiver vazia, inserir v como raiz. 3. Caso contrário, transforme a raiz da árvore na variável para o vértice atual cur_vertex, e compare v com cur_vertex. Se v = cur_vertex, está acabado. 4. se v < cur_vertex então procure o filho à esquerda, caso contrário, procure o filho à direita. Isso é feito mudando cur_vertex para ser o filho à esquerda ou à direita e comparando o novo cur_vertex com v. 5. Eventualmente, não seremos capazes de procurar na direção que a comparação diz que devemos ir. Nesse ponto, insira v como o filho não presente de cur_vertex. A seguir, uma implementação detalhada do algoritmo: <pre> Binsertion := proc(T::graph, x::string,integer) local cur_vertex, V, i, Kids, Left, Right; V := vertices(T); if nops(V) = 0 then addvertex(x, T); T(Root) := x ; # remember the root for later RETURN( x ); fi; </pre> Temos uma árvore com raiz... <pre> cur_vertex := FindRoot(T); while x <> cur_vertex do </pre> As ordenações relativas dos descendentes, x e cur_vertex determinam se x pode ser inserido como folha. <pre> Kids := daughter(cur_vertex,T); Kids := sort( convert(Kids,list) , IsLessThan ); Candidates := sort( [ x, cur_vertex, op(Kids)], IsLessThan ); </pre> Comece com casos fáceis <pre> if nops(Candidates) = 2 then </pre> não há filhos, então adicione apenas um novo vértice. <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; addedge( [cur_vertex,x] , T); cur_vertex := x; break; elif nops(Candidates)=4 then </pre> dois descendentes, então não há inserção nesse nível... <pre> if IsLessThan(x,cur_vertex) then cur_vertex := Kids[1]; else cur_vertex := Kids[2]; fi; next; elif nops(Candidates) = 3 then </pre> não nesse nível se o padrão é [x,L,cur_vertex] ou [L,x,cur_vertex] [cur_vertex,L,x] ou [cur_vertex,x,L] <pre> if Candidates[1] = cur_vertex or Candidates[3] = cur_vertex then cur_vertex := Kids[1]; next; fi; </pre> Para todos os casos restantes adicione x como um novo vértice <pre> if IsLessThan(x,cur_vertex) then addvertex(x,weight=`Lft`,T); else addvertex(x,weight=`Rht`,T); fi; </pre> Sim! Esse nível. <pre> addedge( [cur_vertex,x] , T); cur_vertex := x; break; fi; od; RETURN( cur_vertex ); end: </pre> O procedure addvertex é usado aqui de uma maneira que atualiza os pesos de cada vértice na medida que eles são criados. Isso serve para indicar se o vértice é um descendente à esquerda ou à direita. Enquanto não usamos esses pesos, eles poderiam ser utilizados como uma medida alternativa para ordenação de descendentes de qualquer vértice particular. Ao invés disso, para a ordenação, nós usamos os nomes dos vértices para indicar a ordenação relativa dos vértices. O fato de que quaisquer dois vértices (e não apenas descendentes do mesmo vértice) podem ser comparados nos permite combinar o novo vértice, os descendentes, e o vértice atual tudo em uma lista ordenada que é então inspecionada para determinar qual dos vários casos especiais é relevante durante a inserção de um novo vértice. Qualquer que seja o método de comparação de vértices que for utilizado, é importante que o procedure de comparação seja passado na rotina de ordenação como um argumento extra. Para validar esse procedure, examine como a seguinte lista de inteiros é adicionada: <pre> Num_List:=[4,6,2,8,5,3,7,1]: new(Tree_Num): for i from 1 to 8 do Binsertion(Tree_Num, Num_List[i]); od; </pre> Para ver a árvore resultante e sua estrutura, use o comando Mapledraw. <pre> draw(Tree(4), Tree_Num); </pre> O resultado é claramente uma árvore binária de busca. Árvores binárias existem para serem usadas como método de busca. O procedure BiSearch a seguir faz isso. Declarações do tipo Print foram adicionadas para ilustrar o caminho que o algoritmo usa enquanto faz a busca na árvore. <pre> BiSearch := proc(T::graph, v) local i, Kids, cur_vertex; cur_vertex := FindRoot(T); while v <> cur_vertex do print(cur_vertex); </pre> verifique os casos fáceis <pre> if v = cur_vertex then RETURN(true); fi; Kids := daughter(cur_vertex,T); if Kids = {} then RETURN( false) fi; </pre> descendentes, então comece a procurar... <pre> Kids := sort( convert(Kids,list) ); Candidates := sort( [v , cur_vertex, op(Kids)], IsLessThan); if nops(Candidates) = 4 then # both descendents if IsLessThan(cur_vertex,v) then cur_vertex := Kids[2]; else cur_vertex := Kids[1]; fi; next; # back to top of loop elif nops(Candidates) = 3 then </pre> não está presente, a não ser que cur_vertex seja o primeiro ou último da lista <pre> if Candidates[1] <> cur_vertex and Candidates[3] <> cur_vertex then RETURN( false ); fi; cur_vertex := Kids[1]; next; fi; od; RETURN(true); end: </pre> Para testar esse procedure, tentamos procurar por dois elementos, um que está na árvore e um que não está. Tree_Num; <pre> BiSearch(Tree_Num,8); BiSearch(Tree_Num,12); </pre> === Codificação de Huffman === A codificação de Huffman é um método para construir um código prefixo eficiente para um conjunto de caractéres. Nesse algoritmo, em cada passo os vértices com menos peso são examinados. A codificação de Huffman pode produzir códigos de prefixo em condições ideais. O seguinte pseudocódigo descreve um algoritmo para codificação de Huffman. (Para uma discussão mais completa sobre a codificação de Huffman, veja Cormen, Leiserson, e Rivest, Introduction to Algorithms, MIT Press, 1989.) Comece criando uma lista ordenada de elementos a serem codificados, que tem ordenação com respeito a frequência de ocorrência desses elementos. Considere cada elemento da lista como um vértice com peso igual a sua frequência de ocorrência. 1. Remove os dois primeiros elementos, x e y, dessa lista; 2. Atribua x como o filho à esquerda e y como o filho à direita de um novo vértice em nossa árvore; 3. Atribua o peso de z para ser a soma dos pesos de x e y; 4. Insira z na posição correta de nossa lista, e repita o passo(2). 5. Quando completar, nossa lista deve conter apenas um elemento, que é uma árvore binária com raiz. 6. Novamente, uma rotina de comparação especial é necessária. Elementos do código são representados por listas como em [a,15] and [b,10]. O seguinte procedure HuffCompare compara dois de tais elementos. <pre> HuffCompare :=proc(a::list,b::list) if a[2] <= b[2] then true else false fi; end: </pre> Por exemplo, descobrimos que [b,10] < [a,15]. <pre> HuffCompare([b,10],[a,15]); </pre> Utilizando esse método de comparação, listas de códigos de elementos podem ser ordenadas em ordem crescente. <pre> sort( [[a,5],[b,10],[c,8],[d,11]], HuffCompare); </pre> O algoritmo de codificação de Huffman completo é implementado da seguinte maneira: <pre> Huffman:=proc(L::listlist) local i, j, k, n, Q, T, x, y, z, Temp; new(T); Q := sort( L , HuffCompare ); i := 1; while(nops(Q)>1) do i := i+1; </pre> pegue os dois primeiros elementos de código <pre> x:=Q[1]; Q:=subsop(1=NULL, Q); y:=Q[1]; Q:=subsop(1=NULL, Q); </pre> construa o novo vértice e sua localização <pre> z := [ i , x[2]+y[2]]; for j to nops(Q) while HuffCompare( z, Q[j]) do j := j+1; od; j := j-1; </pre> adicione os vértices e arestas a árvore <pre> Q := [seq(Q[k],k=1..j),z,seq(Q[k],k=j+1..nops(Q))]; addvertex([x[1],y[1],z[1]],weights=[x[2],y[2],z[2]],T); addedge([z[1],x[1]],[z[1],y[1]],T); od; RETURN( eval(T) ); end: </pre> O tipo listlist denota uma lista de listas. O eval no final é incluído para garantir que o resultado retornado pelo procedure é a própria árvore, e não apenas seu nome. Teste esse novo procedure na seguinte lista de caractéres emparelhados com uma frequência relativa de ocorrência; <pre> Huf:=Huffman([[f,15],[b,9],[d,22],[c,13],[a,16],[e,45]]): </pre> Para ver o resultado, novamente utilizamos o comando Mapledraw; <pre> rt := FindRoot(Huf); draw(Tree(rt), Huf); </pre> == Percursos em Árvores == Nessa seção, mostramos como usar o Maple para fazer percursos em árvores. Lembre-se que um algoritmo de percurso em árvore é um procedure para sistematicamente visitar cada vértice de uma árvore ordenada com raiz. Em particular, iremos dar procedures para três importantes algoritmos de percurso em árvore: pré-ordem, ordem, e pós-ordem. Então iremos mostrar como usar esses métodos de percurso para produzir as notações pré-fixa, infixa e pós-fixa para expressões aritméticas. Esses percursos em árvore dependem da construção de árvores ordenadas com raiz. Nós devemos usar os pesos dos vértices para representar a ordem dos filhos, como foi feito nas seções anteriores. Criamos uma árvore desordenada, baseada na árvore da figura 3 da página 5566 do texto, para ver o comportamento dos vários tipos de percurso de árvore. Então o grafo da árvore se torna: d := 'd': <pre> new(Trav): addvertex( [a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p], weights=[0,1,2,3,1,2,1,2,3,1,2,1,2,1,2,3], Trav): addedge( [[a,b],[a,c],[a,d],[b,e],[b,f],[d,g],[d,h], [d,i],[e,j],[e,k],[g,l],[g,m],[k,n],[k,o],[k,p]], Trav): draw(Tree(a),Trav); </pre> Os pesos adicionados aos vértices aqui representam o número desse vértice em relação a seu pai. Por exemplo, d é o terceiro filho de a. Como nas seções anteriores, tais pesos poderiam ser usados para ordenação, apesar de que na verdade, a ordenação é simplesmente alfabética nos nomes dos vértices. Primeiro implementamos o algoritmo de percurso em pré-ordem. Esse algoritmo visita a raiz e então cada sub-árvore da esquerda a direita em uma maneira de percurso em pré-ordem. Em forma de pseudocódigo, o algoritmo para pré-ordem é: 1. Visite a raiz de T. Coloque o atual vértice para ser a raiz. 2. Considere os filhos do vértice atual como raízes das árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. Repita o passo (1) em cada sub-árvore em ordem. Como pode ser visto no passo (2) do pseudocódigo acima, esse algoritmo é recursivo. Nós providenciamos a seguinte implementação em Maple: <pre> Preorder:=proc(G::graph, r) local Dep, v, T; </pre> Visite a raiz <pre> printf(`%a `, r); </pre> Considere os filhos da raiz <pre> Dep:= departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan ); </pre> Percorre em pré-ordem essas sub-árvores em ordem <pre> for v in Dep do Preorder(G, v); od; printf(``, r); end: </pre> A ordem em que nós percorremos os descendentes é determinada pela procedure booleana isLessThan (das seções anteriores). Para percorrer os descendentes em uma ordem diferente, use uma rotina de comparação diferente. Podemos examinar a execução esse procedure no percurso de árvore criado anteriormente, com raiz no vértice A. <pre> Preorder(Trav, a); </pre> Nós implementamos o percurso em ordem de maneira similar. Simplesmente alteramos a sequência na qual os vértices são visitados. Especificamente, olhamos na sub-árvore mais à esquerda dos vértices, seguida pela raiz, e em seguida a sub-árvore mais a direita dos vértices. Em pseudocódigo, temos: 1. Se a árvore T tem apenas um vértice r, então visite r 2. Caso contrário, a árvore T tem mais que um vértice. Chame a sub-árvore mais à esquerda (com raiz no filho mais á esquerda) T_1. Percorre em ordem T_1, então visite a raiz r de T. 3. Percorra em ordem as sub-árvores com raiz T_2,...,T_m. Em Maple, a implementação seria a seguinte: <pre> Inorder:=proc(G::graph, r) local v, Dep, T; </pre> se tivermos atingido um vértice folha, imprima ele <pre> if outdegree(r, G) = 0 then print(r); </pre> Estamos em um vértice interno <pre> else Dep:=departures(r, G); </pre> Determine a ordem dos filhos e percorra a sub-árvore baseada no filho mais à esquerda <pre> Dep := sort( convert( Dep , list ), IsLessThan ); Inorder(G, Dep[1]); </pre> Visite a raiz <pre> print(r); </pre> Percorra em ordem as sub-árvores restantes <pre> for v in Dep[2..nops(Dep)] do Inorder(G, v); od; fi; NULL; end: </pre> Nós adicionamos um NULL como última declaração, para que nada seja retornado. Novamente, para percorrer os filhos em uma ordem diferente, use uma rotina de comparação diferente para ordenar os descendentes. Teste esse novo procedure executando ele na árvore Trav. <pre> Inorder(Trav, a); </pre> O percurso final que implementaremos é em pós-ordem. O percurso em pós-ordem é similar ao percurso em pré-ordem, exceto que apenas visitamos a raiz após visitar cada sub-árvore. Segue o pseudocódigo: 1. Considere os filhos da raiz as sub-árvores T_1, T_2, ... T_m, pegos em ordem da esquerda para a direita. 2. Percorre em pós-ordem T_1, então T_2, até T_m. 3. Imprima a raiz da atual árvore. Em maple, temos o seguinte procedure: <pre> Postorder:=proc(G::graph, r::name) local v,i, Dep, T; </pre> Considere filhos da raiz <pre> Dep:=departures(r, G); </pre> Forme a ordem correta dos filhos <pre> Dep:= sort( convert(Dep,list) , IsLessThan) ; </pre> Percorra em pós-ordem essas sub-árvores em ordem <pre> for v in Dep do Postorder(G, v); od; </pre> Visite a raiz <pre> printf(` %c`, r); end: </pre> Também testamos esse procedure na árvore Trav com raiz A <pre> Postorder(Trav, a); </pre> === Notações Infixa, Pré-fixa e Pós-fixa === Agora iremos discutir como usar o Maple para trabalhar com as formas infixa, pré-fixa e pós-fixa de expressões aritméticas. Essas formas são discutidas na sessão 8.33 do texto. Especificamente, iremos mostrar como criar uma representação em árvore binária de uma expressão infixa, usar percursos em pós-ordem e pré-ordem para crias as formas pós-fixa e pré-fixa de expressões, respectivamente, e também como avaliar essas expressões a partir de suas formas pós-fixa e pré-fixa. Para começar essa seção, nós construímos um procedure em Maple que pega uma expressão aritmética infixa e a converte em uma representação de árvore binária. Essa representação em árvore binária pode ser percorrida usando os percursos das seções anteriores para formar vários formatos de representação aritmética. Como um exemplo, podemos construir uma árvore binária representando uma expressão infixa, como (3+10)^2 - (100-30)/(5*2), e então executar um percurso em pré-ordem nessa árvore para formar a representação pré-fixa dessa expressão aritmética. No procedure em Maple que iremos implementar, consideraremos uma versão modificada da notação infixa, para evitar manipulações complicadas de string que depreciam o verdadeiro problema. Especificamente, iremos utilizar chaves ao invés dos parênteses normalmente utilizados na notação infixa. Adicionalmente, iremos definir nossos operadores em termos de suas representações em string: + para adição, - para subtração e assim vai. Dessa maneira, as facilidades de manipulação de lista do Maple ficam imediatamente disponíveis. Então, nós representamos expressões x + y como [x,"+",y], onde + é uma string. Procedures do Maple podem ser usados para ajudar-nos a construir tais listas. Por exemplo, <pre> `&Plus` := proc(a,b) [a,Unique(`+`),b] end: </pre> nos permite escrever e usar <pre> x &Plus y; </pre> O Maple manipula os nomes de procedures da forma ... como operadores infixos. O procedure que Unique tem é definido especialmente para as rotinas usadas nesse artigo. O resultado é uma lista incluindo versões especialmente codificadas da string +, que não colide com usos anteriores de + como nome. (Cada vértice da expressão árvore deve ter um nome diferente, mesmo que eles pareçam iguais). Similarmente, usamos <pre> `&Times` := proc(a,b) [a,Unique(`*`),b] end: `&Pow` := proc(a,b) [a,Unique(`^`),b] end: `&Div` := proc(a,b) [a,Unique(`/`),b] end: `&Minus` := proc(a,b) [a,Unique(`-`),b] end: </pre> para que possamos escrever e usar <pre> x &Times y, x &Pow y; </pre> Esses podem ser usados para escrever a expressão aritmética ((x+y)^2)+((z-4)/3) como <pre> Expr1:= (((x &Plus y) &Pow 2) &Plus ((z &Minus 4) &Div 3 )); </pre> O resultado é uma lista aninhada de listas, com cada lista representando uma operação binária. Agora estamos prontos para construir uma árvore binária representando tal expressão. O algoritmo necessário para isso em pseudocódigo é: 1. Se houver uma expressão algébrica isolada (como um nome ou número), a árvore consiste de um vértice isolado. 2. Caso contrário, a lista consiste de um operando à esquerda, um operador, e um operando à direita. Use o algoritmo para construir a árvore binária para o operando à esquerda. 3. Repita o passo (2) no operando à direita. 4. Combina os resultados dos passos (2) e (3) para formar a árvore binária. Nossa implementação é esta: <pre> InFixToTree:=proc(L::list,algebraic) local r1,r3, T1, T3,LocL; </pre> Se não tivermos sublistas em nossa expressão, retorne as listas de arestas e vértices como mostrado <pre> if type(L,algebraic) then new(T1); LocL := Unique(L); addvertex(LocL,T1); T1(Root) := LocL; RETURN( eval(T1) ); fi; </pre> L é agora uma lista como [a , + , b] <pre> T1 := InFixToTree(L[1]); r1 := T1(Root); T3 := InFixToTree(L[3]); r3 := T3(Root); </pre> construa a nova árvore <pre> addvertex(vertices(T3),T1); addedge( ends(T3), T1 ); addvertex( L[2] , T1 ): addedge( [L[2],r1], [L[2],r3] , T1 ); T1(Root) := L[2]; RETURN( eval(T1) ); end: </pre> A raiz da árvore é manipulada de maneira especial. Recomputar a raiz da árvore é complicado e taxativo, então nós simplesmente pedimos a cada árvore que se lembre de sua própria raiz. Isso é feito por declarações de tarefa como T(Root) := LocL;. O procedure Unique é novamente usado para se assegurar que cada instancia de vértice tem nome único, mesmo que pareça igual a outro. Em todos os outros aspectos, a implementação é quase exatamente como descrito no pseudocódigo. Para testar esse procedure, use-o para construir uma árvore de expressão para a expressão anterior Expr1. <pre> Expr1; TreeExpr1:=InFixToTree(Expr1): </pre> Para ver isso, desenhe-o como uma árvore. <pre> r := TreeExpr1(Root); draw(Tree(r),TreeExpr1); </pre> Suponha que somos dados uma representação em árvore binária de uma expressão aritmética. Nós podemos usar os algoritmos anteriormente criados para expressas essas árvores como expressões pós-fixas ou pré-fixas executando os percursos de pós-ordem ou pré-ordem, respectivamente. Como isso é trivial, cabe ao leitor explorar essa técnica. Como um exemplo final dessa seção, nós demonstramos como avaliar uma expressão pós-fixa. Para simplificar, nós representamos as operações básicas pelas primeiras letras das palavras add, subtract, multiply, divide e exponentiate. Cabe ao leitor explorar como implementar um procedure que irá avaliar uma expressão pré-fixa, dado que essa técnica é a simples modificação do argumento que será usado no caso pós-fixo. <pre> Post1:=[7,2,3,M,S,4,E,9,3,D,A]; Postfix:=proc(T::list) local i, L; L:=T; while nops(L)>1 do i:=1; while not member(L[i], 'A','S','D','M','E') do i:=i+1; od; if L[i]='A' then L[i]:= L[i-2]+L[i-1]; elif L[i]='M' then L[i]:= L[i-2]*L[i-1]; elif L[i]='S' then L[i]:= L[i-2]-L[i-1]; elif L[i]='D' then L[i]:= L[i-2]/L[i-1]; elif L[i]='E' then L[i]:= L[i-2]^L[i-1]; fi; L := [op(L[1..i-3]),op(L[i..nops(L)])]; od; L; end: Postfix(Post1); </pre> Note que em release 4, nós somos permitidos atribuir diretamente à lista elements. == Árvores e Ordenação == Essa seção explica como usar o Maple para executar e analisar algoritmos de ordenação. Árvores são frequentemente usadas para modelar algoritmos de ordenação, especialmente quando a complexidade desses algoritmos está sendo estudada. Em particular, essa seção foca em dois dos muitos tipos diferentes de algoritmos de ordenação que serão estudados, o bubble sort e o merge sort. === Bubble Sort === Para começar, nós iremos examinar a implementação do bubble sort. A razão pelo qual o bubble sort foi dado o nome "bubble" (bolha) é que o menor da lista "borbulha" em direção a frente da lista, se movendo um passo mais próximo a sua posições após cada iteração. O pseudocódigo, que é destacado na página 575 do texto, é como o seguinte: 1. Recebe como entrada uma lista, L, de n elementos. 2. Entra num laço de repetição do índice i de 1 até n-1. 3. Entra num laço de repetição do índice j de 1 até n-i. 4. Se o elemento na posição j+1 na lista L é menor que o elemento na posição j de L, troque esses dois elementos. No final de cada laço j, nós posicionamos os elementos mais largos i no final de L. O seguinte procedure, chamado BubbleSort, é uma implementação desse pseudocódigo. <pre> BubbleSort:=proc(L::list) local i, j, temp, T; T:= array(L); for i from 1 to nops(L)-1 do for j from 1 to nops(L)-i do if T[j] > T[j+1] then temp:=T[j]; T[j] := T[j+1]; T[j+1] := temp; fi; od; od; convert(T,list); end: </pre> Note que antes de começar a mover os elementos, nós convertemos a lista L em um arranjo. Isso porque nós podemos mudar cada elemento de um arranjo em apenas uma operação, enquanto para mudar qualquer parte de uma lista, nós devemos recopiar a lista inteira -- um processo envolvendo n operações. Quando nós terminarmos de mover os elementos, nós transformamos o arranjo novamente em uma lista. Podemos examinar a execução desse procedure em uma lista desordenada. Note que se a lista tem cinco elementos, um total de 5*4/2 = 10 laços são usados pelo algoritmo bubble sort para ordenar esses elementos. <pre> BubbleSort([3,2,4,1,5]); </pre> === Merge Sort === Nós iremos agora implementar o merge sort em Maple. Usaremos também o Maple para estudar a complexidade desse algoritmo. O algoritmo de merge sort pode ser implementado como um procedure recursivo. A idéia básica da tarefa de aplicar merge sort numa lista é de dividi-la em duas de tamanhos iguais ou quase iguais, ordenando cada sub-lista usando o algoritmo merge sort, e no final juntando as listas resultantes. Isso pode ser descrito no seguinte pseudocódigo: 1. Dada uma lista de elementos, se o tamanho da lista é 1, retorne essa lista. 2. Se a lista tem mais de dois elementos, use o merge sort nessas duas listas e retorne a lista fundida resultante. Primeiro usamos um procedure, Merge, que pega duas listas ordenadas e funde elas em uma única lista ordenada, consistindo dos elementos das duas listas. Aqui está o exemplo em código Maple para o procedure de Merge: <pre> Merge := proc(L1::list, L2::list) local L, i,j,k,m,n; L:=[]; i := 1; j := 1; k := 1; m := nops(L1); n := nops(L2); L := array(1..m+n); while i <= m and j <= n do if L1[i] <= L2[j] then L[k] := L1[i]; i := i+1; else L[k] := L2[j]; j := j+1; fi; k := k+1; od; while i <= m do L[k] := L1[i]; i := i+1; k := k+1; od; while j <= n do L[k] := L2[j]; j := j+1; k := k+1; od; convert(L,list); end: </pre> Nós ilustramos o uso desse procedure com o seguinte exemplo: <pre> Merge([1,2,6,8],[3,5,7]); </pre> Agora nós damos o pseudocódigo para o algoritmo merge sort: A descrição do algoritmo merge sort que nós iremos usar é baseada na definição recursiva. Em pseudocódigo: 1. Se a lista L tem apenas um elemento, ela está ordenada, então retornamos L. 2. Se L tem mais de um elemento, nós dividimos a lista em duas listas de mesmo tamanho, ou de maneira que a segunda lista tenha exatamente um elemento a mais que a primeira. 3. Nós recursivamente usamos o merge sort nas duas listas, e então juntamos as duas listas. <pre> MergeSort:=proc(L::list) local First, Second,i, n; </pre> Se a lista tem apenas um elemento, retorne-o <pre> if nops(L) = 1 then </pre> print(L) <pre> L; else </pre> A lista tem mais de um elemento print(L) <pre> n := nops(L); mid := floor(n/2); </pre> Divida as listas em duas sub-listas de tamanho igual <pre> First := L[1..mid]; Second := L[mid+1..n]; </pre> Junte o resultado dos merge sorts nas duas listas <pre> Merge(MergeSort(First), MergeSort(Second)); fi; end: </pre> Nós ilustramos o uso do procedure MergeSort ordenando uma lista desordenada com 100 elementos: <pre> MergeSort([8,2,4,6,9,7,10,1,5,3]); </pre> Nós iremos agora analizar o tempo de execução do MergeSort em relação ao BubbleSort. Especificamente, nós iremos criar uma lista desordenada com 1000 elementos aleatórios e executar o BubbleSort e o MergeSort nessa lista. Isso irá nos dar uma ilustração limitada do tempo de execução desses procedures, na qual o leitor deve expandir lendo análises teóricos no texto. Para criar uma lista aleatória com 100 elementos, usamos os comandos rand e seq do Maple, como representado a seguir: <pre> A:=[seq(rand(), i=1..100)]: </pre> Então, nós podemos usar o comando time para medir a quantidade de tempo necessário para ordenar a lista aleatória: <pre> st:=time(): BubbleSort(A): time() - st; st:=time(): MergeSort(A): time() - st; </pre> O leitor é encorajado a implementar outros tipos de algoritmos de ordenação usando o Maple e a estudar a complexidade relativa desses algoritmos quando usandos para ordenar listas de vários tamanhos diferentes. Também é interessante usar técnicas de animação para ilustrar os passos dos algoritmos de ordenação. Apesar de não fazermos isso aqui, o leitor com habilidades avançadas de programação é convidado a fazê-lo. Atente-se à importância de usar a estrutura de dados correta, ex: lists vs arrays. == Árvores de Extensão == Essa seção explica como usar o Maple para construir árvores de extensão (spanning trees) para grafos e como usá-las para resolver muitos tipos diferentes de problema. Árvores de extensão já foram usadas no capítulo 7; elas tem uma grande quantidade de aplicações. Em particular, nós iremos mostrar como usar o Maple para formar essas árvores usando dois algoritmos: depth-first search (busca em profundidade) e breadth-first search (busca em largura). Então, mostraremos como usar o Maple para fazer backtracking, uma ténica baseada em depth-first search, usada para resolver vários problemas. Para começar, discutiremos como implementar o algoritmo de depth-first search no Maple. Como o nome do algoritmo sugere, os vértices são visitados em ordem crescente de profundidade na árvore de extensão. O pseudocódigo é: 1. Dado um grafo G, e uma raiz vértice v, considere o primeiro vizinho de v, chamado w. Adicione aresta(v, w) a árvore de extensão. 2. Escolha x para ser um vizinho de w que não está na árvore. Adicione aresta(x,w) e faça conjunto w = x. 3. Repita o passo (2) até não ter mais vértices que não estão na árvore. A implementação do depth-first search é a seguinte: <pre> Depth := proc(G::graph, r) local v, V, N, S,In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while In_Tree <>[] do v := In_Tree[-1]; N:=neighbors(v, G) minus vertices(S); if N = {} then In_Tree := In_Tree[1..nops(In_Tree)-1]; next; fi; addvertex(N[1],S); addedge([v,N[1]],S); In_Tree:=[op(In_Tree), N[1]]; od; eval(S); end: </pre> Demonstramos o uso do procedure depth-first search com o seguinte exemplo: <pre> new(G1): addvertex(A,B,C,D,E,F,G,H,I,J,K,L,M, G1): addedge( A,B,A,D,B,C,B,E,C,F,D,E, D,H,E,F,E,I,F,G,F,J,G,L, G,J,H,K,H,I,I,J,I,K,M, K, G1); S1:=Depth(G1,E): draw(Tree(E), S1); </pre> Tendo implementado o algoritmo de árvore de extensão de depth-first search, agora nós podemos modificar levemente o código Maple e conseguir uma árvore de extensão com breadth-first search. O algoritmo de breadth-first search opera examinando todos os vértices da atual profundidade do grafo antes de se mover para baixo, no próximo nível do grafo. Antes de implementar esse algoritmo, nós damos uma descrição em pseudocódigo dele. 1. Dado um grafo G, e uma raiz vértice v, identifique os vizinhos de v. Chame esse vizinho de conjunto N_1. 2. Adicione arestas de V para cada vértice em N_1 que ainda não está na árvore de extensão. 3. Pegue o primeiro vértice de N_1, chamado w. Considere os vizinhos de w; chame-o de conjunto de vizinhos N_2. 4. Repita o passo (2) com w substituído por v, e N_2 substituído por N_1. 5. Se todos os vértices em N_1 tiverem sido usados, mova abaixo para o próximo nível, e repita o passo (2). A seguir, uma implementação em Maple do algoritmo breadth-first search, chamada Breadth; <pre> Breadth:=proc(G::graph, r) local v, N, S, In_Tree; new(S); addvertex(r, S); In_Tree:=[r]; while not(In_Tree=[]) do v := In_Tree[1]; N:=neighbors(In_Tree[1], G) minus vertices(S); for v in N do addvertex(v,S); addedge([In_Tree[1], v],S); In_Tree:=[op(In_Tree), v]; od; In_Tree:= In_Tree[2..nops(In_Tree)]; od; eval(S); end: S2:=Breadth(G1, E): draw(Tree(E), S2); </pre> Note que as duas árvores de extensão são diferentes mesmo que elas tenham raiz no mesmo vértice. Em particular, a árvore com depth-first search tem uma estrutura funda e magra, enquanto a árvore com breadth-first search é menor e mais larga. Essas representações gráficas ajudam a ilustrar o algoritmo utilizado, e heuristicamente, nós podemos usar as representações para adivinhar qual dos dois algoritmos foi usado. === Backtracking === Backtracking é um método que pode ser usado para resolver problemas que poderiam ser impráticos de se solucionar usando técnicas de busca excessiva. O backtracking é baseado na busca sistemática pela solução de um problema usando uma árvore de decisão. Aqui nós mostramos como usar backtracking para reslver vários problemas diferentes, incluindo pintar um grafo, resolver o problema das n-rainhas, que consiste em posicionar n rainhas em um tabuleiro de xadrez nXn de maneira que uma rainha não possa atacar a outra, e também o problema do subconjunto da soma, que visa encontrar um subconjunto de um conjunto de inteiros cuja soma é um inteiro dado. O primeiro problema que nós iremos atacar através de um procedure de backtracking é o de colorir um grafo usando n cores, sendo n um inteiro positivo. Dado um grafo, nós tentaremos colorir ele usando n cores de uma maneira gananciosa. No entanto, quando nós atingimos uma coloração que não nos permite colorir um vértice adicional propriamente, nós usamos backtrack, mudando a cor de um vértice anteriormente colorido e tentando novamente. Aqui está o pseudocódigo para nosso procedure BackColor que executa essa coloração baseado em backtracking. Aqui nós ordenamos as cores como color1, color2, ..., colorn: 1. Ordene os vértices do grafo G como v_1, v_2, ..., v_m. 2. Atribua color1 a v_1. Sete i = 2. 3. Atribua color c a v_i, onde c é o menor inteiro para que nenhum vizinho de v_i já tenha sido atribuído com color c. 4. Se nós pudermos atribuir tal cor a v_i, incremente i e repita o passo (3). 5. Se nós não pudermos atribuir nenhuma cor a v_i, nós usamos o backtrack, setando i = i-1 e incrementando a cor de v_i, se possível. 6. Se nós não tivermos uma coloração válida, repita o passo (5). 7. Para quando tivermos colorido todos os vértices, ou usado todas as colorações possíveis. Uma implementação desse pseudocódigo no seguinte algoritmo Maple, chamado BackColor: <pre> BackColor := proc(G::graph,n::integer) local i,k, v, V, cur_vertex, Assigned, Available, used , N, cur_color; V:= convert(vertices(G), list ); </pre> inicialize as cores atribuídas e disponíveis <pre> for v in V do Assigned(v):=0; Available(v):=[seq(k, k=1..n)]; od; cur_vertex:=1; while cur_vertex >= 1 and cur_vertex <=nops(V) do v := V[cur_vertex]; </pre> Atribua a menor cor ao vértice atual. Reuna todos os vizinhos do vértice atual. <pre> N:=neighbors(v, G); while Assigned(v)=0 and Available(v) <> [] do Used := map( Assigned , N ); if not member( Available(v)[1], Used ) then Assigned(v) := Available(v)[1]; fi; Available(v) := Available(v)[2..nops(Available(v))]; od; </pre> Faça um backtrack se tal cor não existir <pre> if Assigned(v) = 0 and (Available(v) = []) then printf(`Backtracking on %a %d`, v, Assigned(v)); while (Available(v)= []) and cur_vertex > 1 do Available(v) := [seq(k, k=1..n)]; Assigned(v) := 0; cur_vertex := cur_vertex - 1; v := V[cur_vertex]; od; if cur_vertex > 1 then Assigned(v) := 0; else break; fi; else cur_vertex:=cur_vertex+1; fi; od; if not has( map( Assigned , V ), 0 ) then for v in V do printf(`Assign vertex %a color %d`, v, Assigned(v)); od; else printf(`There does not exist a proper vertex coloring`); printf(`with %a colors`, n); fi; end: </pre> Nós agora iremos testar essa implementação em um novo grafo chamado C1. Note que a saída do procedure BackColor é a atual atribuição de cores em qualquer estágio do backtracking, e a coloração final ou indicação de não-existência de possibilidade de coloração própria para o caso diante da terminação do procedure. <pre> new(C1): addvertex([E,B,C,D,A], C1): addedge(A,B,A,E,B,C,B,D,B,E,C,D,D,E,C1): BackColor(C1,3); </pre> Adiante, nós vamos examinar a execução do procedure BackColor em C1, com duas novas arestas adicionadas. Note que esse novo grafo tem K_4 como subgrafo. <pre> addedge(A,D,A,C, C1): BackColor(C1,3); BackColor(C1,4); </pre> Outro problema que pode ser solucionado com backtracking é o de posicionar n-rainhas em um tabuleiro de xadrez de dimensão nXn de maneira que nenhuma rainha tenha como atacar a outra. Isso significa que nenhum par de rainhas pode ser posicionado na mesma linha horizontal, vertical ou diagonal. Nós iremos resolver esse problema usando um procedure baseado em backtracking. Iremos posicionar as rainhas no tabuleiro até que todas elas estejam posicionadas ou não haja mais posição disponível para um rainha ficar sem estar na mesma linha diagonal, vertical ou horizontal que outra já posicionada. Para fazer com que o procedure principal seja mais fácil de entender, iremos criar um procedure ajudante, que irá verificar se um particular lugar do tabuleiro é válido. Se houverem duas rainhas na mesma linha, coluna ou diagonal, então ValidQuenns irá retornar false; caso contrário, o procedure irá retornar true. <pre> ValidQueens:=proc(Q::matrix, row::integer, col::integer, size::integer) local i,return_value; return_value:=true; </pre> Verifique se as dimensões são válidas <pre> if row > size or col > size then return_value := false; else </pre> Cheque as rainhas horizontalmente. Note que o algoritmo principal nunca posiciona duas rainhas na mesma coluna. então checagem vertical não é necessária. <pre> for i from 1 to col-1 do if Q[row, i] = 1 then return_value:=false; fi; od; </pre> Cheque as rainhas em duas diagonais. <pre> for i from 1 to col-1 do if row>i then if Q[row-i, col-i] = 1 then return_value:=false; fi; fi; if row+i <=size then if Q[row+i, col-i] = 1 then return_value:= false; fi; fi; od; fi; </pre> Retorne o valor <pre> return_value; end: </pre> O procedure principal para resolver o problema das n-rainhas, que será chamado de NQueens, segue o mesmo fluxo de controle que o procedure BackColor, como pode ser deduzido nos comentários em-linha. Especificamente, nós temos um estágio de inicialização, ou incremental, onde tentamos preencher a atual coluna, e o estágio de backtracking, onde nós usamos backtrack se não pudermos posicionar a rainha na atual coluna. A implementação Maple desse procedure é a seguinte: <pre> NQueens:=proc(n::integer) local cur_col, cur_row, Q, bad_position, Assigned; </pre> Inicie Queens <pre> Q:=linalg[matrix](n, n, 0); cur_col:=1; Assigned:=[]; while cur_col >= 1 and cur_col <=n do </pre> Atribua uma rainha a próxima coluna <pre> bad_position := true; cur_row:=0; </pre> a primeira posição disponível funciona? <pre> while cur_row < n and bad_position do cur_row := cur_row+1; bad_position := false; </pre> bad é true se houver um vizinho com vértice colorido <pre> Q[cur_row, cur_col] := 1; if not ValidQueens(Q, cur_row, cur_col, n) then bad_position := true; Q[cur_row, cur_col] := 0; fi; od; </pre> Usa backtrack se não tiver nenhum posição disponível pra rainha. <pre> if cur_row=n and bad_position then printf(`Backtracking on column`); printf(` %d of %a since stuck`, cur_col, Q); while not ValidQueens(Q, cur_row, cur_col, n) and cur_col > 1 do cur_col := cur_col-1; Q[Assigned[cur_col], cur_col]:=0; cur_row := Assigned[cur_col] + 1; Assigned:=subsop(cur_col=NULL, Assigned); od; if cur_col >= 1 and cur_row <= n then Assigned:=[op(Assigned), cur_row]; Q[cur_row, cur_col] := 1; cur_col := cur_col + 1; else cur_col := cur_col - 1; fi; else </pre> Se o posicionamento da rainha é atualmente válido, mova para a próxima <pre> cur_col:=cur_col+1; Assigned:=[op(Assigned), cur_row]; fi; od; if (cur_col >= 1) then printf(`A proper Queen placement is %a`, Q); else printf(`No Queen placement with %d Queens`, n); fi; end: </pre> Agora nós usamos o procedure NQueens para resolver o problema de n-rainhas quando n = 3 e n = 4. <pre> NQueens(3); NQueens(4); </pre> Nós consideramos um terceiro problema que pode ser resolvido usando backtracking; o problema do subconjunto da soma. Dado um conjunto de inteiros S, nós desejamos encontrar um subconjunto B de S tal que a soma dos elementos de B é dado valor M. Para usar backtracking para resolver esse problema, nós sucessivamente selecionamos inteiros de S até a soma desses elementos seja igual a M ou exceda M. Caso exceda M, nós usamos backtrack removendo o último elemento da soma, e inserimos um valor diferente. Antes de implementarmos o procedure principal, nós iremos criar duas pequenas funções ajudantes que ajudam na manipulação de listas. O primeiro procedure ajudante, chamado de ListSum determina a soma dos elementos em dada lista. <pre> ListSum:=proc(S::list, Ind::list) local i, T; T:=0; for i from 1 to nops(Ind) do T:=T+S[Ind[i]]; od; T; end: </pre> A segunda função ajudante, chamada de ListInd determina um subconjunto de uma lista S que é indicada pelas posições armazenadas na lista J. <pre> ListInd:=proc(S::list, J::list) local i, T; T:=[seq(S[J[i]],i=1..nops(J))]; end: </pre> O procedure principal para determinar a possível solução para o problema do subconjunto da soma, chamado SubSum, é o seguinte <pre> SubSum:=proc(S::list, M::integer) local CurSub, next_index, T, Ind, CurSum,i; </pre> Inicializa variáveis <pre> Ind:=[]; CurSum:=0; i:=1; next_index:=0; T:=S; </pre> Repetir o laço até alcançar o valor de soma dada. <pre> while not (CurSum = M) do printf(`The current subset %a has sum %d`, ListInd(T, Ind), CurSum); next_index:=next_index+1; </pre> se alcançarmos um impasse, use backtrack. <pre> if next_index > nops(T) and Ind[nops(Ind)] = nops(T) then Ind:=subsop(nops(Ind)=NULL,Ind); Ind:=subsop(nops(Ind)=NULL,Ind); CurSum:=ListSum(T, Ind); else </pre> se não houverem valores para a cima, utiliza-se backtrack. <pre> if next_index > nops(T) then next_index:=Ind[nops(Ind)]+1; Ind:=subsop(nops(Ind)=NULL, Ind); CurSum:=ListSum(T,Ind); fi; </pre> Se o atual subconjunto é menor que M, então adicionamos o próximo valor ao subconjunto; <pre> if CurSum+T[next_index] < M then Ind:=[op(Ind), next_index ]; CurSum:=ListSum(T, Ind); fi; fi; </pre> Se tivermos usado todos os índices, setar as variáveis para valores genéricos. <pre> if Ind=[] then T:=subsop(1=NULL, T); break; fi; od; </pre> Retorna a lista sum <pre> ListInd(T,Ind); end: </pre> Executamos esse procedure no Exemplo 6 na página 588 do texto: <pre> SubSum([31,27,15,11,7,5], 39); </pre> Os três problemas foram atacados usando backtracking. Colorindo grafos, o problema das n-rainhas e o subconjunto da soma representam um vasto número de problemas que podem ser resolvidos usando backtracking, e o leitor irá certamente encontrar ocasiões onde as técnicas dessa sessão irão ajudar a resolver tais problemas. == Árvores de Extensão Mínima == Essa sessão explica como usar o Maple para achar a árvore de extensão mínima de um grafo ponderado. Lembre-se que uma árvore de extensão mínima T de um grafo ponderado G é uma árvore de extensão de G com o mínimo peso de todas as árvores de extensão de G. Os dois algoritmos mais conhecidos para construção de árvores de extensão mínimas são chamados de algoritmo de Prim e algoritmo de Kruskal; nós iremos desenvolver procedures em Maple que implementam ambos os algoritmos aqui. Iremos começar estudando o algoritmo de Prim. O algoritmo de Prim procede construindo uma árvore sucessivamente selecionando uma aresta de peso mínimo que extende essa árvore de seu atual conjunto de vértices. O pseudocódigo é o seguinte: 1. Comece a construir a árvore de extensão mínima T com a aresta de menor peso de todo o grafo. 2. Adicione a T a aresta de menor peso que é incidente ao vértice em T que não forma um circuito simples em T. 3. Repita o passo (2) até que nós tenhamos um total de n-1 arestas em T. Para simplificar nossa implementação do algoritmo de Prim, primeiro criamos um procedure chamado de MinWeight, que determina a aresta de menor peso com exatamente um vértice em dado conjunto de vértices. <pre> MinWeight:=proc(G::graph, S::set) local e, i, Candidates, Del, Min_Edge; </pre> Determine o conjunto de arestas adjacentes. <pre> if S=vertices(G) then Candidates:=edges(G) else Candidates := incident(S,G); fi; if Candidates = {} then RETURN(NULL) fi; </pre> Determine a aresta candidata de menor peso. <pre> Min_Edge:=Candidates[1]; for e in Candidates do if eweight(Min_Edge,G) > eweight(e ,G) then Min_Edge:=e; fi; od; RETURN(Min_Edge); end: </pre> O caso especial de todos os vértices de G está incluído para dar um ponto de partida convenient para o algoritmo de Prim. Nesse caso, nós simplesmente retornamos a aresta de G de menor peso. Em todos os outros casos, a busca é restrita a arestas emanando do subgrafo induzido pelos vértices especificados. A implementação depende do fato de que o procedure incidente ache todas as arestas e deixe um conjunto de vértices particular. Ainda, a eficiência geral do algoritmo pode ser melhorada analisando-se sistematicamente uma lista ordenada de arestas, ao invés de procurar por novos candidatos a cada passo. Dado o procedure MinWeight, a real implementação do algoritmo de Prim é direto ao ponto. Primeiro inicializamos a árvore mínima ponderada T para ser a árvore com apenas uma aresta, sendo essa aresta a de menor peso. A cada passo adicionamos uma aresta de peso mínimo que seja incidente com a atual árvore T. <pre> Prim := proc(G::graph) local i, VT, V, T, e; new(T); V := vertices(G); </pre> Adicione a aresta de menor peso. <pre> e := MinWeight(G,V); addvertex(ends(e, G), T); addedge(ends(e,G), T); </pre> Repita o laço até que todas as arestas n-1 sejam adicionadas a árvore. <pre> for i from 2 to nops(V)-1 do e := MinWeight(G,vertices(T)); if e = NULL then ERROR(`no spanning tree`) fi; </pre> Adicione um novo vértice e uma nova aresta. <pre> addvertex(ends(e,G) minus vertices(T), T); addedge(ends(e,G),weights=eweight(e,G),T); od; RETURN( eval(T) ); end: </pre> Nós retornamos eval(T) ao invés de apenas T, para se assegurar de que a própria árvore seja passada, e não apenas seu nome. Para testar o procedure Prim, nós encontramos uma árvore de extensão mínima do grafo ponderado do Exemplo 1 da página 595 do texto. Você pode construir o grafo usando os comandos: <pre> new(City1): addvertex(sf,chic,den,ny,atl,City1): addedge( [sf,ny,sf,chic,sf,den,sf, atl], weights=[2000,1200,900,2200], City1): addedge( [den,chic,den,ny,den, atl], weights=[1300,1600,1400], City1): addedge( [chic,ny,chic,atl, atl, ny], weights=[1000,700, 800], City1): </pre> Então, a árvore de extensão mínima é T1, dada por: <pre> T1 := Prim(City1): </pre> Essa árvore é melhor vista como uma árvore selecionando uma rai particula e então desenhando a árvore. <pre> draw(Tree(sf), spantree(T1,sf)); </pre> O total peso de suas arestas pode ser computada como: <pre> total := 0: for e in edges(T1) do total := total + eweight(e,T1) od: total; </pre> O algoritmo de Kruskal constrói a árvore ponderada minima adicionando sucessivamente uma aresta de peso mínimo que não forma um circuito simples em nenhum dos fragmentos de árvore previamente construídos. O pseudocódigo para esse algoritmo é: 1. Ordene as arestas do grafo em ordem crescente. 2. Escolha a aresta de menor peso, e. 3. Se e cria um ciclo T quando adicionado, descarte e da lista e repita o passo (2). 4. Adicione e a árvore ponderada de peso mínimo T. 5. Repita o passo (2) até que a árvore tenha n-1 arestas. Antes de podermos implementar o algoritmo de Kruskal, precisamos ser capazes de ordenar arestas. Como nas sessões anteriores, podemos fazer isso usando as rotinas de ordenação embutidas no Maple, dando um ótimo procedura para comparação de quaisquer duas arestas. A rotina de comparação necessária aqui é sutilmente mais complicada que a de antes, pois ela deve usar o grafo em adição com os nomes das arestas dentro da comparação. Isso pode ser feito usando o procedure template, como pode se observar a seguir. Um grafo específico é substituído por um placeholder em um template. <pre> edgecompare := proc(G::graph) subs(TESTG=eval(G) , proc(a,b) if eweight(a,TESTG) <= eweight(b,TESTG) then true else false fi; end ); end: </pre> Chamando esse procedure em um grafo específico, como City1, nós criamos o procedure comparison customizado para esse grafo. <pre> comp1 := edgecompare(City1): </pre> Pode ser usado como <pre> comp1(e1,e2); </pre> Agora para ordenar a lista de arestas de City1 por peso, tudo que precisamos fazer é: <pre> edgelist := convert(edges(City1),list); edgelist := sort(edgelist,comp1); </pre> Os pesos dessa lista ordenada estão em ordem crescente, como verificado mapeando o comando eweight na lista. <pre> map( eweight , edgelist , City1); </pre> Armados com essa rotina de ordenação, estamos quase prontos para implementar o algoritmo de Kruskal. A cada passo do algoritmo, temos uma aresta e, uma coleção de árvores T, formada por arestas de G, e G, e nós devemos determinar se a aresta e forma um ciclo. Isso é feito encontrando os componentes de T, e checando cada componente para ver se ambos os lados da aresta e estão no mesmo componente. Isso é feito pelo procedure: <pre> InComponent := proc(e,T::graph,G::graph) local c,C; C := components(T); for c in C do if ends(e,G) minus c = {} then RETURN(true); fi; od; RETURN(false); end: </pre> Ele faz uso do fato de que os comandos components representam cada componente por um conjunto de vértices. Agora estamos prontos para implementar o algoritmo de Kruskal. <pre> Kruskal:=proc(G::graph) local E,T,i,n,e; E := convert( edges(G), list); # sort the edges E := sort( E, edgecompare(G)); </pre> comece a construir a floresta <pre> new(T); i := 0; n := nops(vertices(G)): while i < n and E <> [] do e := E[1]; if InComponent( e , T , G ) then E := subs(e=NULL,E); next; fi; </pre> adicione uma nova aresta a floresta <pre> addvertex(ends(e,G),T); addedge(ends(e,G),T); i := i+1; E := subs(e=NULL,E); od; eval(T); # the new tree end: </pre> Esse algoritmo também pode ser testado na árvore City1, do exemplo 1 na página 595. <pre> T2 := Kruskal(City1): draw(Tree(sf), spantree(T2,sf)); </pre> == Computações e Explorações == 1. Mostre todas as árvores com seis vértices. '''''Solução''''' Para resolver esse problema nós usamos uma definição recursiva de árvores. Sabemos que um gráfo vazio é uma árvore, e que um grafo com apenas um vértice é uma árvore. Podemos então construir árvores mais largas a partir dessas árvores menores se pegarmos cada vértice e formarmos uma nova árvore adicionando uma folha conectada a esse vértice. Então, nós devemos criar um procedure em Maple chamado ExtendTree, que pega um conjunto de árvores com n vértices e adiciona uma nova aresta a cada árvore, retornando o conjunto resultante de árvores com n+1 vértices. A implementação em Maple é a seguinte: <pre> ExtendTree:=proc(Trees::set) local i, j, S, t, num_vertices, X; S:={}; </pre> Entra num laço sobre todas as árvores em dado conjunto <pre> for i to nops(Trees) do T := Trees[i]; </pre> Adiciona novo vértice <pre> num_vertices:=nops(vertices(T)); addvertex(num_vertices+1, T); </pre> Para cada vértice, adicione uma nova aresta folha. <pre> for v in vertices(T) do new(X[i][v]); X[i][v]:=duplicate(T); addedge([v , num_vertices+1], X[i][v]); S:=S union X[i][v]; od; od; S; end: </pre> Iremos agora ilustrar como formar todas as árvores com 4 vértices, e deixar a determinação de todas as árvores de tamanho mais largo serem determinadas pelo leitor: <pre> new(StartingTree): addvertex(1, StartingTree): X:=ExtendTree(ExtendTree(ExtendTree(StartingTree))): draw(Tree(1),X[1]); draw(Tree(1), X[2]): draw(Tree(1),X[3]): draw(Tree(1), X[4]): draw(Tree(1),X[5]): draw(Tree(1), X[6]): </pre> 2. Construa uma codificação de Huffman para as letras da língua inglesa baseada na frequência de sua ocorrência em textos comuns em inglês. '''''Solução''''' Esse problema pode ser quebrado em dois problemas menores. O primeiro problema é determinar como coletar a frequência de ocorrência para cada letra da língua inglesa. O segundo é como construir uma codificação de Huffman baseado nessa frequência de ocorrencia. Nós já criamos o procedure de Huffman em Maple que pode ser usado para determinar a codificação de Huffman correta dada a frequência de ocorrencia de caractéres em inglês. Consequentemente, nós resolvemos o segundo problema. Para resolver o primeiro problema, nós podemos usar o Maple para analisar uma string de texto e conta o número de ocorrencia de cada letra do alfabeto inglês. Especificamente, podemos usar strings em Maple da seguinte maneira. Suponha que nós temos uma passagem de texto que está em minúsculo e não tem pontuação. como: <pre> input_text:= `the quick brown fox sat down and had lunch with me`; </pre> Então, podemos inicializar a tabela indexada com cada carácter da língua inglesa, e então analisar input_text e contar as ocorrências de cada carácter. Inicialização <pre> alphabet:=`a bcdefghijklmnopqrstuvwxyz`; for i from 1 to length(alphabet) do freq[substring(alphabet, i..i)]:=0; od: </pre> Conta a ocorrência de cada carácter <pre> for i from 1 to length(input_text) do freq[substring(input_text, i..i)] := freq[substring(input_text, i..i)] + 1; od: freq[a]; freq[e]; freq[q]; </pre> Para determinar a frequência de ocorrência das letras inglesas em certos contextos, nós podemos rodar esse programa em largas amostras de inputs. Podemos simplesmente extender nosso alfabeto e incluir pontuação e qualquer outro carácter especial que seja usado no conjunto dos caracteres. Vocè irá encontrar uma distribuição um pouco diferente para tipos diferentes de conteúdo, como literatura, correspondencia, programas de computador, e-mail etc. Vale notar que muitos livros sobre Criptografia contêm frequências de caracteres em inglês e muitas outras linguagens. Além disso, esse código pode ser usado para contar a frequência de ocorrência de qualquer conjunto de caracteres, como ASCII, francês, espanhol etc. 3. Compute o número de diferentes árvores de extensão de K_n para n = 1, 2, 3, 4, 5, 6. Conjecture uma fórmula para o número de tais árvores de extensão sempre que n for um inteiro positivo. '''''Solução''''' Esse problema pode ser resolvido facilmente usando o comando counttrees do Maple, que retorna o número de árvores de extensão únicas de um grafo não-dirigido. Então, para determinar o número de árvores de extensão únicas em K_n, n = 1..6, podemos executar as seguintes declarações em Maple. <pre> counttrees(complete(1)); counttrees(complete(2)); counttrees(complete(3)); counttrees(complete(4)); counttrees(complete(5)); counttrees(complete(6)); </pre> Deixamos para o leitor a conjectura da forma. Uma dica útil é para procurar por uma fórmula de forma n^{f(n)}, onde f(n) é uma simples função em termos de n. 4. Compute the number of different ways n queens can be arranged on an n \times n chessboard so that no two queens can attack each other for all positive integers n not exceeding 10. '''''Solução''''' Esse problema pode ser resolvido alterando o procedure NQueens que foi implementado nesse capítulo. Especificamente, quando uma solução é determinada, ao invés de sair do procedura, nós simplesmentes fazemos backtrack nessa solução e continuamos, até todos os caminhos possíveis terem sido examinados. Assim, o procedura vai sair apenas quando todas as soluções tiverem sido testadas. Deixamos para o leitor alterar o procedure NQueens e conjecturar a fórmula para o número de soluções em termos de n para o problema das n-rainhas. 5. Desenhe a árvore de jogo completa para damas em um tabuleiro 4x4. '''''Solução''''' Iremos oferecer uma solução parcial para esse problema; o leitor deve completar a solução. Especificamente, iremos criar um procedure em Maple chamado MovePiece que irá determinar todas as possíveis combinações de movimento dada uma peça específica que deve ser movida para algum espaço. Quando esse procedure for criado, o leitor deve determinar como representar essas posições no tabuleiro como vértices e arestas, como determinar o próximo nível da árvore jogo e se é necessária alguma condição de parada. A implementação de MovePiece é bastante direta: examinamos cada peça que pode ser movida, e então determinamos se podemos mover a peça para frente e para esquerda ou direita, dependendo da posição atual da peça no tabuleiro e se há uma peça ocupando a possivelmente nova posição. Além disso, nós vamos determinar se uma peça pode pular e capturar uma peça de um oponente, dependendo do espaço do tabuleiro e a posição do oponente. Adicionalmente, nós vamos examinar ser a peça é rei, nesse caso, a peça pode mover tanto para frente como para trás no tabuleiro. Agora damos uma implementação em Maple de MovePIece. Comentários em linha são dados para facilitar o entendimento do código. <pre> MovePiece:=proc(A::matrix, piece::integer) local i, j, k, cur_column, is_king, S, Temp, direction; </pre> Inicialize os valores, dependendo do valor da peça <pre> S:=[]; if piece = 1 then direction:=-1; else direction:=1; fi; </pre> Examine todas as posições possíveis no tabuleiro <pre> for i from 1 to 4 do for j from 1 to 4 do </pre> Se tivermos achado um peça, determine se é ou não o rei. <pre> if abs(A[i,j])=piece then if A[i,j] < 0 then is_king:=1; else is_king:=0; fi; </pre> Se a peça é um rei, então examine as direções pra frente e pra trás <pre> for k from 0 to is_king do if k>0 then direction:=-1*direction; fi; </pre> Examine possíveis novas posições para ver se elas ainda estão no tabuleiro <pre> if i+direction >= 1 and i+direction <= 4 then for cur_column from -1 to 1 by 2 do if j-cur_column >=1 and j-cur_column<=4 then </pre> Determine se a posição está livre <pre> if A[i+direction, j-cur_column] = 0 then </pre> Mova uma única posição <pre> Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=piece; S:=[op(S), copy(Temp)]; elif abs(abs(A[i+direction,j-cur_column]) -piece)=1 then </pre> Nós podemos ser capazes de pular uma peça <pre> if (i+2*direction >=1 and i+2*direction<=4) and (j-2*cur_column >=1 and j-2*cur_column<=4) then </pre> Pule uma peça <pre> if A[i+2*direction, j-2*cur_column] = 0 then Temp:=copy(A); Temp[i,j]:=0; Temp[i+direction, j-cur_column]:=0; Temp[i+2*direction, j-2*cur_column] :=piece; S:=[op(S), copy(Temp)]; fi; fi; fi; fi; od; fi; od; if is_king=1 then direction:=-1*direction; fi; fi; od; od; </pre> Procura por reis <pre> for i from 1 to nops(S) do for j from 1 to 4 do if S[i][1,j] = 1 then S[i][1,j]:=-1 fi; if S[i][4,j] = 2 then S[i][4,j]:=-2 fi; od; od; </pre> Retorna lista de novas combinações do tabuleiro <pre> S; end: </pre> Para examinar esse procedure, nós vamos criar uma combinação inicial no tabuleiro, chamada A, usando a função matriz do Maple. <pre> A:=linalg[matrix](4, 4, [[2,0,2,0],[0,0,0,0],[0,0,0,0],[0,1,0,1]]); </pre> == Exercícios e Projetos == Como proposto pelo professor Umberto Rivieccio, da turma 02 da disciplina de Fundamentos Matemáticos para a Computação II, período 2016.1, aqui serão implementados dois exercícios em java, sugeridos pelo material original. '''1'''. Desenvolver um método para listar os vértices de uma árvore ordenada com raiz em "level order". '''''Solução''''' Para realizar o exercício, vamos implementar a estrutura de dados de uma '''árvore binária''' através de duas classes: a classe '''Arvore''' e a classe '''No'''. Os vértices da árvore (da classe No) serão ordenados pela sua '''chave''' (int) e armazenarão um inteiro no campo '''valor'''. Além disso, terão campos que guardarão os '''filhos'''. Além do método construtor, criaremos um método para imprimir cada nó da árvore. Para poder imprimir os valores em level order, será preciso utilizar uma '''fila'''. Podemos utilizar a classe LinkedList do Java. <pre> class No { public int chave; public int valor; public No filhoEsquerdo; public No filhoDireito; public No(int chave, int valor, No left, No right){ this.chave = chave; this.valor = valor; this.filhoEsquerdo = left; this.filhoDireito = right; } public void printLevelOrder(){ System.out.print(this.valor); LinkedList<No> nos = new LinkedList<>(); if(this.filhoEsquerdo != null) nos.add(this.filhoEsquerdo); if(this.filhoDireito!= null) nos.add(this.filhoDireito); while(!nos.isEmpty()){ No n = nos.remove(); System.out.print(", " + n.valor); if(n.filhoEsquerdo != null) nos.add(n.filhoEsquerdo); if(n.filhoDireito != null) nos.add(n.filhoDireito); } } } </pre> A classe Arvore guardará a raiz da árvore. Temos um método para inserir os valores de forma que a árvore continue ordenada. <pre> class Arvore { public No raiz; public Arvore(){ this.raiz = null; } public void inserir(int chave, int valor){ No n = raiz; No pai = null; while(n != null){ if(n.chave > chave) { pai = n; n = n.filhoEsquerdo; } else if (n.chave < chave){ pai = n; n = n.filhoDireito; } else { return; } } if(pai == null){ this.raiz= new No(chave, valor, null, null); } else { if(pai.chave > chave) pai.filhoEsquerdo = new No(chave, valor, null, null); else pai.filhoDireito = new No(chave, valor, null, null); } } public void printLevelOrder(){ if(raiz != null) raiz.printLevelOrder(); System.out.println(); } } </pre> Se rodarmos o seguinte código: <pre> Arvore a = new Arvore(); a.inserir(5, 10); a.inserir(6, 12); a.inserir(3, 6); a.inserir(7, 14); a.inserir(1, 2); a.inserir(9, 18); a.inserir(4, 8); a.printLevelOrder(); </pre> Obtemos o seguinte resultado: <pre> 10, 6, 12, 2, 8, 14, 18 </pre> '''2'''. Implemente o insertion sort em Maple. '''''Solução''''': <pre> InsertionSort:= proc(L::list) local A:= < L >, j, key, i; for j from 2 to nops(L) do key:= A[j]; for i from j by -1 to 2 while A[i-1] > key do A[i]:= A[i-1] end do; A[i]:= key end do; convert(A, list) end proc; </pre> == Referências == Rosen, Kenneth H. (2002). "[http://www.mhhe.com/math/advmath/rosen/r5/student/ch09/maple.html Discrete Mathematics and Its Applications]". Material de apoio. Suplemento do Maple para o capítulo 9. Acesso em 29 de Maio de 2016. ddc25e466b40e6301d5aa6d86d879c4553992bc2 Relações 0 190 764 2016-05-30T13:26:41Z Clah 19 Created page with "Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando co..." wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === 5b87c63bab853f5d07196b541d16767a37e59a80 772 764 2016-06-01T11:36:35Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> 03331c64cf5c6bc0408774d37779c581201a7da5 773 772 2016-06-01T11:39:07Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == 77fd95df1f3d7eefc7d52d0c08c86b90813cd544 774 773 2016-06-01T11:49:21Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> 361e5fb9d31dcde12c78341d2b594f74138b3a39 775 774 2016-06-01T11:54:53Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' fc3a54d68077718e87d4028bea7c723f024d76cd 776 775 2016-06-01T11:55:08Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' 9a5c53a82ebfc859f9166336969013018b78380a 777 776 2016-06-01T11:55:58Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito. <pre> AllRelations := proc(S::set) local s, t, # indices into S C; # Cartesian product SxS C := {}; for s in S do for t in S do C := C union [s,t]; od; od; combinat[powerset](C); end: </pre> f424bdb2760c426b486cd10a8419f522d6f2a2af 778 777 2016-06-01T11:56:21Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito. <pre> AllRelations := proc(S::set) local s, t, # indices into S C; # Cartesian product SxS C := {}; for s in S do for t in S do C := C union [s,t]; od; od; combinat[powerset](C); end: </pre> 6b068bb35ef8938dafb0299853e6d60d8a79460e 779 778 2016-06-01T11:57:37Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito. <pre> AllRelations := proc(S::set) local s, t, # indices into S C; # Cartesian product SxS C := {}; for s in S do for t in S do C := C union [s,t]; od; od; combinat[powerset](C); end: </pre> Nós agora testar o nosso procedimento em um conjunto menor, de modo a manter a saída de um comprimento razoável. O leitor é convidado para determinar o tempo de execução e o comprimento de saída para o processo, quando o conjunto de entrada tem cardinalidade 4 ou 5. Tenha em mente que existem <math>2^{n^{2}}</math> relações em um conjunto com n membros. <pre> AllRelations(1,2); </pre> ff2bc6f6692b1681f8394c4e2954b705de8b4897 780 779 2016-06-01T12:07:44Z Clah 19 wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito. <pre> AllRelations := proc(S::set) local s, t, # indices into S C; # Cartesian product SxS C := {}; for s in S do for t in S do C := C union [s,t]; od; od; combinat[powerset](C); end: </pre> Nós agora testar o nosso procedimento em um conjunto menor, de modo a manter a saída de um comprimento razoável. O leitor é convidado para determinar o tempo de execução e o comprimento de saída para o processo, quando o conjunto de entrada tem cardinalidade 4 ou 5. Tenha em mente que existem <math>2^{n^{2}}</math> relações em um conjunto com n membros. <pre> AllRelations(1,2); </pre> 2- Determine quantas relações transitivo existem em um conjunto com ''n'' elementos para todos os inteiros positivos ''n'' com ''n'' ≤ 7. '''Solução:''' Vamos construir cada possíveis <math>N x N</math> da matriz zero-um, usando um algoritmo semelhante ao de contagem binária. O pseudocódigo é: 1- Para cada valor de 1 até <math>2^{(n ^{2})}</math>, criamos uma lista que é a representação de base 2 desse inteiro. 2- Nós criamos uma matriz M com elementos sendo a lista de valores. Estes são todos os possíveis <math>n^{2}</math> de matriz zero-um (o leitor pode provar esta afirmação). 3- Avaliar o fechamento transitivo de cada uma das matrizes que criamos, e retornar o conjunto de fechos transitivos dessas matrizes. A implementação é como se segue: <pre> FindTransitive := proc(S::set) local i, j, T, P; P := {}; for i from 0 to 2^(nops(S)^2)-1 do T := convert(i, base, 2); while nops(T) &lt; nops(S)^2 do T := [op(T), 0]; od; P := P union matrix(nops(S), nops(S), T); od; P; end: </pre> Mais uma vez, utilizamos o nosso processo em valores relativamente pequenos (devido ao comprimento da saída), e deixamos a exploração adicional para o leitor. <pre> P := FindTransitive(1,2): Q := {}: for i from 1 to nops(P) do Q := Q union Warshall(P[i]): od: Q; </pre> 3- Encontre o fecho transitivo de uma relação em um conjunto com pelo menos 200 elementos. '''Solução:''' Vamos gerar aleatoriamente uma matriz zero-um com dimensão 10x10, e em seguida, aplicar o algoritmo de \textbf{Warshall} para deduzir o fecho transitivo matriz. Para gerar uma matriz zero-um aleatória, usamos a função '''randmatrix''' do pacote '''linalg''', e fornecer seu terceiro argumento, opcional com um procedimento que gera uma sequência aleatória 0's (zero) e 1's (uns). Em seguida, aplicamos o algoritmo de '''Warshall''' a esta matriz aleatória, obtendo o resultado. Aqui, para economizar espaço, podemos aplicar este procedimento para uma matriz de 10x10 como um exemplo. <pre> Q := randmatrix(10, 10, entries=rand(2)); Warshall(Q); </pre> == Referências == [http://www.mhhe.com/math/advmath/rosen/r5/student/ch07/maple.html Maple: Chapter 7. Relations] da41a518417219995026751db464381f299da0d0 781 780 2016-06-01T12:08:38Z Clah 19 /* Cálculos e explorações */ wikitext text/x-wiki Neste artigo, vamos aprender a usar Maple para trabalhar com relações binárias e n-árias. Nós explicaremos como usar Maple para representar relações binárias usando conjuntos de pares ordenados, usando representações utilizando de matrizes zero-um, e usando grafos dirigidos. Mostraremos como usar Maple para determinar se uma relação tem várias propriedades que utilizam estas diferentes representações. Nós descreveremos como calcular o fechamento das relações. Em particular, mostraremos como encontrar o fechamento transitivo de uma relação utilizando dois algoritmos diferentes, e nós iremos comparar o tempo necessário para usar esses algoritmos. Depois de explicar como usar o Maple para trabalhar com relações de equivalência, mostraremos como usar o Maple para trabalhar com ordenações parciais. Mostraremos como usar Maple para fazer ordenação topológica e para determinar se uma ordenação parcial é um reticulado. Concluiremos mostrando como usar o Maple para encontrar relações de ordenações parciais. == Uma Introdução às Relações em Maple == O primeiro passo para entender as relações e sua manipulação em Maple é determinar como representar as relações em Maple. O leitor notará que não há nenhum pacote de relações específico presente em Maple, e, portanto, a implementação e a representação das relações em Maple pode assumir a forma mais conveniente para a questão em apreço. Possíveis representações de relações em Maple incluem conjuntos de pares ordenados, matrizes zero-um ou gráficos dirigidos, entre muitos outros. Para este capítulo, vamos examinar representações de par ordenado e matrizes zero-um, bem como a representação de gráfico direcionado. Em primeiro lugar, devem representar relações como pares ordenados. Para este fim, nós construímos um typerel estruturado para as relações. (Note-se que não se pode utilizar a relação nome, uma vez que este tipo já é usado pela biblioteca do Maple). De acordo com a definição, uma relação é um conjunto de pares ordenados de qualquer tipo e de objeto qualquer. O seguinte tipo Maple reflete essa definição. <pre> `type/pair` := [anything, anything]; `type/rel` := set(pair); </pre> Isso é útil, uma vez que nos permite assegurar que, quando passar argumentos para funções, temos utilizado o tipo de dados correto para a entrada. Sendo o nosso tipo rel estruturado, é muito mais fácil escrever rel do que escrever set(anything, anything) após cada argumento de que deve ser interpretado como uma relação. Isso também pode ser feito À mão dentro de cada função, antes do processamento real começar, mas o Maple fornece este tipo verificação automática para nós e resulta em um código mais rápido e, mais importante para nós, mais legível. Para um exemplo específico, suponha que desejamos estabelecer uma relação que é definida em termos de restrições numéricas, como no Exemplo 4 na página 3577 do texto. No exemplo texto, precisamos criar uma relação R no domínio A=(1,2,3,4) de modo que R={(a,b)|a divide b}. Vamos construir esta relação ao examinar cada possível par ordenado de elementos, e admitindo pares ordenados em R se, e somente se, o par ordenado satisfaz a condição apropriada. Chamaremos R de '''DividesRelation'''. <pre> DividesRelation := proc(A::set(integer)) local i, j, temp, R; R := {}; for i in A do for j in A do if (gcd(i,j) = i) then R := R union [i, j]; fi; od; od; RETURN(R); end: DividesRelation(1,2,3,4); </pre> Por conveniência, nós também definir a seguinte variação. <pre> DivRel := proc(n::posint) local i; DividesRelation(seq(i,i=1..n)); end: </pre> Este procedimento constrói a relação divides sobre o conjunto de todos os inteiros no conjunto {1,2,3,...,n}. Será conveniente ter à mão o seguinte procedimento para criar o dual ou opposite relação de uma determinada relação. <pre> DualRelation := proc(R::rel) local u; map(u -&gt; [ u[2], u[1] ], R); end: </pre> Isto simplesmente inverte todos os pares que pertencem à relação. == Determinar Propriedades de Relações usando Maple == Maple pode ser utilizado para determinar se uma relação tem uma propriedade específica, tal como a reflexividade, simetria, antissimetria e transitividade. Isto pode ser realizado através do procedimento de criação do Maple que tomam como entrada a determinada relação, examinando os elementos da relação, e determinar se a relação satisfaz a propriedade dada. Desde que possamos utilizar isso repetidamente, será conveniente ter uma rotina que irá extrair para nós do domínio de qualquer relação. Nós simplesmente coletamos em conjunto todos os pontos que ocorrem tanto como uma primeira ou uma segunda entrada em algum par na relação. (Não que, estritamente falando, isso não precise ser igual ao domínio da relação, Uma vez que podem, na verdade, existirem pontos no domínio que não são R-relacionados para qualquer outro ponto do domínio. Talvez seja melhor chamar a isto o ''effective domain'' (domínio efetivo) da relação. <pre> DomainRelation := proc(R::rel) RETURN(map(u-&gt;op(1,u), R) union map(u-&gt;op(2,u), R)); end: </pre> Em primeiro lugar, vamos examinar como determinar se uma relação é reflexiva. <pre> IsReflexive := proc(R::rel) local is_reflexive, # return value u; # index into Dom(R) is_reflexive := true; for u in DomainRelation(R) do is_reflexive := is_reflexive and member([u,u], R); od; RETURN(is_reflexive); end: </pre> <pre> R1 := [1,1], [1,2], [2,1], [2,2], [3,4], [4,1], [4,4]: R2 := [1,1], [1,2], [2,1]: IsReflexive(R1); IsReflexive(R2); </pre> Vamos examinar as propriedades simétricas e antissimétricas nos próximos dois procedimentos. Para determinar se uma relação é simétrica vamos simplesmente usar a definição; isto é, verificar se para cada par (a,b) em uma relação, o par (b,a) é também membro da relação. Se descobrirmos um par (a,b) na relação para o qual o par (b,a) não está na relação, então sabemos que a relação não é simétrica. Caso contrário, deve ser simétrica. Esta é a lógica utilizada pelo procedimento seguinte. <pre> IsSymmetric := proc(R::rel) local i, is_symmetric; is_symmetric := true; for i from 1 to nops(R) do if not member( [ R[i][2], R[i][1] ], R) then is_symmetric := false; fi; od; RETURN(is_symmetric); end: </pre> Para determinar se uma determinada relação R é anti-simétrica, voltamos a usar a definição. Lembre-se que para uma relação R que ser anti-simétrica, ele deve ter a propriedade de que, sempre que um par (a,b) pertencer a R, e o par (b,a) também pertence a R, então devemos ter que a=b. Para verificar isso, nós simplesmente vamos varrer todos os membros u=(a,b) de R, e veja se o par oposto (b,a) pertence a R. Se isso acontecer, e se a≠b, então R não pode ser antissimétrica; caso contrário, ela é. <pre> IsAntiSymmetric := proc(R::rel) local u; # index into R for u in R do if member([op(2,u), op(1,u)], R) and op(1, u) &lt;&gt; op(2, u) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora usamos os nossos procedimentos para determinar qual das relações definidas anteriormente são simétricas ou antissimétricas. <pre> IsSymmetric(R1); IsSymmetric(R2); IsAntiSymmetric(R1); IsAntiSymmetric(R2); R3 := [1,1], [1,2], [1,4], [2,1], [2,2], [3,3], [4,1], [4,4]: R4 := [2,1], [3,1], [3,2], [4,1], [4,2], [4,3]: IsAntiSymmetric(R3); IsAntiSymmetric(R4); </pre> Para decidir se uma relação R é transitiva, devemos verificar se (a,c) pertencerá a R quando existirem pares (a,b) e (b,c) que pertencem a R. Fazemos isso examinando todos os pares (a,b) em R, em conjunto com todos os elementos de x do domínio D de R, para ver se um par (b,x) existe em R para o qual o par (x,b) não está em R. Se encontrarmos essa combinação, em sequencia, a relação R não pode ser transitiva; de outra forma, R será. <pre> IsTransitive := proc(R::rel) local DomR, # domain of R u,v,w;# indices into DomR for u in DomR do for v in DomR do for w in DomR do if (member([u,v], R) and member([v,w], R) and not member([u,w], R)) then RETURN(false); fi; od; od; od; RETURN(true); end: IsTransitive(R1); IsTransitive(R2); IsTransitive(R3); IsTransitive(R4); </pre> == Relações n-árias em Maple == Usando Maple, podemos construir uma relação n-ária, onde n é um número inteiro positivo. O formato para a expressão relação n-ária em Maple é semelhante à da relação 2-ária no Maple. Por exemplo, considere a seguinte relação 4-ária que representa os registros dos alunos. <pre> M1:=[Adams, 9012345,`Politics`, 2.98], [Woo, 9100055, `Film Studies`, 4.99], [Warshall, 9354321, `Mathematics`, 3.66]: </pre> O primeiro campo representa o nome do aluno, o segundo campo é o número de identificação do aluno, o terceiro campo é o departamento ao qual o estudante pertence e, finalmente, o último registro armazena a média do estudante. Com esse exemplo de como usar as relações n-árias, nós iremos construir um procedimento geral para calcular uma determinada projeção de uma relação. O procedimento toma como entrada um conjunto de n-tuplas (enupla) como relação com a imagem, que está a ser projetada sobre. A saída deste processo é um conjunto de m-tuplas. <pre> MakeProjection := proc(R::set, P::list) local i, j, S, temp_list; S := {}; for i from 1 to nops(R) do temp_list := []; for j from 1 to nops(P) do temp_list := [ op(temp_list), R[ i ][ P[j] ] ]; od; S := S union temp_list; od; S; end: </pre> Agora vamos examinar este procedimento projeção sobre a relação 4-ário que foi construído anteriormente nesta seção. <pre> MakeProjection(M1, [3,4,1]); MakeProjection(M1, [2,4]); </pre> Passamos agora de construir as projeções para a construção de junção das relações. A operação de junção tem aplicações em comandos de banco de dados quando as tabelas de informações precisam ser combinadas de forma significativa. A operação de junção que vamos programar em Maple segue este esquema pseudocódigo: 1- Entrada: duas relações de entrada A e B, e um parâmetro inteiro não negativo p; 2- Analisar cada elemento de A, e determinar os últimos campos de p de cada elemento; 3- Examinar todos os elementos, y, da relação B para determinar se os primeiros campos de p em y coincidem com os últimos campos p de elemento x; 4- Ao encontrar uma correspondência de um elemento em A e um elemento em B, que combinam estes elementos, colocando o resultado em C, que é devolvido como saída. <pre> MakeJoin := proc(p, A, B) local i, j, k, C, list_A, list_B, x, ret_elem, is_done; list_A := []; list_B := []; C := {}; for i from 1 to p do list_B := [op(list_B), i]; list_A := [nops(B[1])-i, op(list_A)]; od; for i from 1 to nops(A) do; is_done := false; x := MakeProjection(A[i], list_A); j := 1; while j &lt;= nops(B) and is_done = false do if MakeProjection(B[j], list_B) = x then ret_elem := A[i]; for k from p+1 to nops(B[j]) do ret_elem := [op(ret_elem), B[j][k] ]; od; is_done := true; fi; j := j+1; od; C := C union ret_elem; od; C; end: </pre> Nós examinamos como este procedimento funciona no exemplo definido no livro, nas páginas 371 e 372, envolvendo cursos que os professores estão ensinando para determinar onde eles estão localizados no campus. <pre> A:=[Cruz, Zoology, 335], [Cruz, Zoology, 412], [Farber, Psychology, 501], [Farber, Psychology, 617], [Grammer, Physics, 544], [Grammer, Physics, 551], [Rosen, Computer, 518], [Rosen, Mathematics, 575]: B:=[Computer, 518, N521, 14], [Mathematics, 575, N502, 15], [Mathematics, 611, N521, 16], [Physics, 544, B505, 16], [Psychology, 501, A100, 15], [Psychology, 617, A110, 11], [Zoology, 335, A100, 9], [Zoology, 412, A100, 8]: MakeJoin(2, A, B); </pre> == Representar relações como dígrafos e Matrizes Zero-Um == Como foi dito no início deste capítulo, o Maple permite representar e manipular as relações em uma variedade de formas. Vimos como o pacote '''combinat''' do Maple permite que o produto cartesiano possa ser usado para gerar relações e, nesta seção, vamos utilizar os pacotes '''networks''' (redes) e '''linalg''' para representar e manipular as relações. Nós explicamos como usar Maple para trabalhar com relações usando a representação das relações como conjuntos de pares ordenados. Nesta seção, vamos mostrar como usar o Maple para trabalhar com as relações usando dois métodos alternativos para representar essas relações. Em primeiro lugar, vamos explicar como representar relações como grafos dirigidos; isto requer o uso do pacote ''Maple'' '''networks'''. Em segundo lugar, vamos mostrar como para representar as relações utilização de matrizes zero-um; isto requer o uso do pacote ''Maple'' '''linalg'''. Veremos que o uso dessas representações alternativas de relações nos permite usar uma ampla gama de capacidades do Maple para resolver problemas que envolvem as relações. === Representando Relações Usando Grafos Dirigidos === Começamos analisando a forma de representar as relações usando grafos dirigidos em Maple, com a ajuda do pacote '''networks'''. Para começar, vamos carregar o pacote '''networks'''. <pre> with(networks): </pre> Agora, podemos converter nossas relações que foram representadas no formato de par ordenado em um grafo dirigido usando o seguinte algoritmo simples, chamado '''MakeDigraph'''. <pre> MakeDigraph := proc(A::set,R::set) local G; new(G); addvertex(A, G); addedge(R, G); RETURN(G); end: G1 := MakeDigraph(1,2,3,4,R1); R1; </pre> O procedimento novo do pacote ''networks'' cria uma ocorrência de um gráfico, e ''addedge'' faz exatamente o que seu nome sugere: ele adiciona uma borda para o gráfico que é seu segundo argumento. (Uma discussão mais completa dessas rotinas serão apresentados no Capítulo 7.) Podemos agora usar essa representação gráfica da relação R1 para deduzir se é ou não é transitiva. Para fazer isso, usamos o algoritmo ''all-pairs shortest path'' (todos os pares de caminho mais curto), denotado como ''allpairs'' em Maple. Especificamente, desejamos determinar que se uma borda (a,b) e uma borda (b,c) ocorrem no gráfico, então a borda (a,c) deve ocorrer também. Por isso, usamos o seguinte esboço pseudocódigo. 1- Entrada: um gráfico G, que representa uma relação R; 2- Executar o algoritmo ''all-pairs shortest path'' em G, que retorna o caminho mais curto entre dois pontos quaisquer; 3- Se há um par de elementos que tem comprimento finito que é maior do que 1, então sabe que o grafo não é reflexivo; 4- Caso contrário, temos tudo (finitos) comprimentos entre os elementos um, de modo que, se (a,b) é um par e (b,c) é um par, então a distância de a para c é 1 (Uma vez que este é o único comprimento possível, já que foram eliminados os comprimentos finitos que são maiores do que 1, no passo 3), por isso (a,c) tem de ser uma borda, e, portanto (a,c) está na relação R. 5- Saída: o valor da decisão a partir das etapas 3 e 4. A implementação deste pseudocódigo é a seguinte; <pre> IsTransitive_G := proc(G::graph) local i, j, S, T, is_trans; is_trans := true; T := allpairs(G); S := vertices(G); for i from 1 to nops(S) do; for j from 1 to nops(S) do; if T[S[i],S[j] ] &gt; 1 and T[S[i],S[j] ] &lt;infinity then is_trans := false fi; od; od; is_trans; end: IsTransitive_G(G1); R2; IsTransitive_G(MakeDigraph(1,2,3,4,R2)); </pre> Você deve examinar outras formas de manipular a representação gráfica das relações em Maple. Em particular, depois de ter estudado o Capítulo 7, você pode explorar uma variedade de maneiras de manipular gráficos, e ver como as informações sobre as relações podem ser extraídos a partir deles. === Relações representando Usando Matrizes Zero-One === Vamos agora considerar a representação das relações de matrizes zero-um. Para começar, uma vez que estará trabalhando com matrizes, é preciso indicar que estaremos usando as funções do pacote ''Maple'' '''linalg'''. Especificamente, teremos de usar a função de '''matrix''' (matriz), e as operações da matriz relacionados, do pacote '''linalg'''. <pre> with(linalg): </pre> Agora, nós fornecemos um procedimento Maple, que encontra a representação de matriz zero-um de uma relação, dados os pares ordenados nesta relação. O pseudocódigo para este algoritmo é como se segue; <pre> \item{Entrada: o conjunto R e domínio D.} \item{Para cada par (i,j) em D, que determinar se a (i,j) está em R.} \item{Se o par está em R, colocamos uma entrada 1 na posição que representa (i,j) na matriz M. Caso contrário, coloca-se um 0 na posição que representa (i,j) na matriz M.} \item{Saída: M.} </pre> Traduzindo esse algoritmo para o Maple, o código resulta no seguinte procedimento. <pre> MakeMatrix := proc (R::set, D::set) local i, j, L; L := []; for i from 1 to nops(D) do for j from 1 to nops(D) do if member([i,j],R) then L := [op(L), 1] else L := [op(L),0]; fi; od; od; evalm(matrix(nops(D), nops(D), L)); end: </pre> Em seguida, vamos converter as relações definidas no início deste capítulo de sua forma conjunto para representação de forma zero-um. <pre> m1:=MakeMatrix(R1,1,2,3,4); m2:=MakeMatrix(R2,1,2,3,4); m3:=MakeMatrix(R3, 1,2,3,4); m4:=MakeMatrix(R4,1,2,3,4); </pre> Agora que temos as representações de zero-um da matriz dessas relações, podemos usar essas matrizes para determinar se as relações são reflexivas, simétricas e/ou antissimétricas. Nesta forma, é um pouco mais fácil para determinar se uma a relação tem alguma destas propriedades. Aqui, por exemplo, é um procedimento em Maple que determina se uma relação é reflexiva, utilizando a sua representação matricial de zero-um. <pre> IsReflexive_M:= proc(M::matrix) local i, is_reflex; is_reflex := true; for i from 1 to coldim(M) do if M[i,i] = 0 then is_reflex := false; fi; od; is_reflex; end: IsReflexive_M(m1); IsReflexive_M(m3); </pre> Aqui também estão as versões de matriz para '''IsSymmetric''' e '''IsAntiSymmetric'''. <pre> IsSymmetric_M := proc(M::matrix) local i,j; # row and column indices if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i-1 do if M[i,j] &lt;&gt; M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Deve ser quadrada. O código para verificar antissimetria é semelhante. <pre> IsAntiSymmetric_M := proc(M::matrix) local i,j; if rowdim(M) &lt;&gt; coldim(M) then RETURN(false); fi; for i from 1 to rowdim(M) do for j from 1 to i - 1 do if M[i,j] = 1 and M[i,j] = M[j,i] then RETURN(false); fi; od; od; RETURN(true); end: </pre> Mais uma vez, podemos determinar se as relações representadas em forma de matriz zero-um são simétricas ou antissimétricas. <pre> IsSymmetric_M(m1); IsAntiSymmetric_M(m1); IsSymmetric_M(m2); IsAntiSymmetric_M(m2); IsSymmetric_M(m4); IsAntiSymmetric_M(m4); </pre> == Fecho Computacional de Relações == Determinar o fecho de uma relação em Maple pode ser abordado basicamente da mesma forma que abordamos o problema de determinar as propriedades das relações. Especificamente, deve implementar algoritmos que utilizam técnicas semelhantes para determinação das propriedades reflexivas e simétricas, a fim de determinar o fecho dessas propriedades relacionais. O fecho transitivo da relação vai exigir mais conhecimento, mas vamos analisar vários métodos para determinar o fecho transitivo de uma determinada relação. === Fecho reflexivo === O algoritmo para calcular o fecho reflexivo de uma relação é realmente muito simples. Nós simplesmente definimos cada entrada diagonal em sua representação matricial igual a 1. A matriz resultante representa o fecho reflexivo da relação. <pre> RefClose := proc(M::matrix) local i; for i from 1 to coldim(M) do M[i,i] := 1; od; evalm(M); end: </pre> Agora usamos '''RefClose''' para encontrar o fecho reflexivo de algumas das relações que introduzimos como exemplos no início do capítulo. <pre> RefClose(m1); RefClose(m4); </pre> === Fecho simétrico === Em seguida, vamos construir um processo de determinação do fecho simétrico de uma relação R. Novamente, usamos a observação, a partir da página 3822, que observa que, se (a,b) é um elemento de R, então (b,a) é um elemento do fecho simétrico de R. O código em Maple, o qual é semelhante ao fecho reflexivo implementado acima, é: <pre> SymmClose := proc(M::matrix) local i, j; for i from 1 to coldim(M) do for j from 1 to rowdim(M) do if M[i,j] = 1 then M[j,i] := 1; fi; od; od; evalm(M); end: </pre> Este procedimento pode ser usado para localizar o fecho simétrico da alguns dos nossos exemplos anteriores, como se segue. <pre> SymmClose(m1); SymmClose(m4); </pre> === Fecho transitivo === Tendo criado os fechos mais simples, das propriedades reflexivas e simétricos, nós agora nos concentraremos na implementação do fecho transitivo em Maple, que é um problema mais difícil do que os casos anteriores em termos de complexidade computacional. No texto, há dois algoritmos descritos, ou seja, um fecho transitivo genérico e um algoritmo de Warshall, e ambos serão abordados nesta seção. Para implementar o fecho transitivo, precisamos implementar tanto o juntar booleano quanto as operações de produto booleanas, previamente introduzido no Capítulo 2. Para começar, vamos criar as funções auxiliares booleana que nos permitem converter entre zero e um e valores verdadeiro-falso. <pre> with(linalg): int_to_bool(0) := false: int_to_bool(1) := true: bool_to_int(true) := 1: bool_to_int(false) := 0: </pre> Em seguida, vamos construir o booleano se juntar a função, novamente com base no trabalho anterior do capítulo 3. <pre> BoolJoin := proc(A::matrix, B::matrix) local i, j, C; C := matrix(rowdim(A), coldim(A), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(A) do C[i,j] := int_to_bool(A[i,j]) or int_to_bool(B[i,j]); od; od; map(bool_to_int,C); end: </pre> Após isso, nós construímos o produto booleano. <pre> BoolProd := proc(A::matrix, B::matrix) local i, j, k, C; C := matrix(rowdim(A), coldim(B), zeroes); for i from 1 to rowdim(A) do for j from 1 to coldim(B) do C[i,j] := false; for k from 1 to coldim(A) do C[i,j] := C[i,j] or (int_to_bool(A[i,k]) and int_to_bool(B[k,j])); od; od; od; map(bool_to_int, C); end: </pre> Agora estamos prontos para começar a aplicar o procedimento para o cálculo do fecho transitivo, tal como definido na página 3877 do texto. <pre> TransClosure := proc(M::matrix) local i, A, B; A := M; B := A; for i from 2 to coldim(M) do A := BoolProd(A, M); B := BoolJoin(B, A); evalm(A); evalm(B); od; evalm(B); end: </pre> Nós testamos nosso procedimento de fecho transitivo em um exemplo. <pre> T1 := matrix(3,3,[1,0,1,0,1,0,1,1,0]): TransClosure(T1); </pre> Em seguida, vamos examinar como o algoritmo de Warshall compara (em termos de tempo de execução de um exemplo simples) para este algoritmo geral que acabamos de implementar. Em primeiro lugar, temos de implementar o algoritmo de Warshall em Maple. <pre> Warshall := proc(M::matrix) local i, j, k, W, n; W := map(int_to_bool,M); n := coldim(M); for k from 1 to n do for i from 1 to n do for j from 1 to n do W[i,j] := W[i,j] or (W[i,k] and W[k,j]); od; od; od; evalm(map(bool_to_int, W)); end: Warshall(T1); </pre> Podemos comparar estes dois procedimentos em termos de tempo de execução usando o comando do tempo no Maple. Mas, devemos notar que esta comparação em um único exemplo não prova nada; ao contrário, ela é útil para ilustrar geralmente os tempos de execução para os dois algoritmos que foram implementados. Para fazer essa ilustração, vamos criar uma matriz zero-um que opera sobre o conjunto A = {1,2,3,4}. <pre> T2:=matrix(4, 4, [0,0,0,1,1,0,1,0,1,0,0,1,0,0,1,0]); st:=time():Warshall(T2):time()-st; st:=time():TransClosure(T2):time()-st; </pre> A partir deste exemplo, podemos ver que no algoritmo do Warshall pode ser uma melhoria substancial sobre o método que utiliza junção e produtos booleanos, neste exemplo específico. O leitor é encorajado a explorar mais que este. == Relações de equivalência == Examinaremos, nesta seção, como podemos usar o Maple para calcular com relações de equivalência. Há três problemas específicos que vamos abordar aqui: como calcular a classe de equivalência de um elemento, dada uma relação de equivalência em um conjunto; como determinar o número de relações de equivalência em um conjunto finito; e, como calcular a relação de equivalência menor que contém uma dada relação em algum conjunto finito. Para começar, vamos primeiro fornecer um teste para uma relação estar em uma relação de equivalência. Usando o trabalho que já fizemos, e recordando que uma relação de equivalência é simplesmente aquele que é reflexiva, simétrica e transitiva, o nosso trabalho é simples. <pre> IsEqRel := IsTransitive @ IsSymmetric @ IsReflexive; </pre> Recorde-se que, tendo uma relação de equivalência de R, e um membro a do domínio de R, a classe de equivalência de um é o conjunto de todos os membros b do domínio de R para o qual o par (a,b) pertence a R. Em outras palavras, é o conjunto de todos os elementos no domínio de R que são R-equivalentes para a. Assim, o algoritmo usado para construir a classe de equivalência de um é muito simples: nós apenas iremos pesquisar R procurando todos os pares da forma (a,b), acrescentando cada um desses segundo elemento b para a classe. Não precisamos procurar por pares da forma (b,a), porque as relações de equivalência são simétricas. Dada uma relação de equivalência, e um ponto no seu domínio, esse procedimento retorna a classe de equivalência do ponto. <pre> EqClass := proc(R::set, a::anything) local i, S; S := {}; for i from 1 to nops(R) do if R[i][1] = a then S := S union R[i][2]; fi; od; RETURN(S); end: EqClass([0,0],[0,2],[1,0],[1,1], [2,1],[1,2],[0,1], 0); </pre> Agora apresentamos um procedimento que constrói todas as relações de equivalência em um determinado conjunto. <pre> DetermineEqClass := proc(A::set) local P, Q, S, E, i, j, p; S := {}; E := {}; for i from 1 to nops(A) do for j from 1 to nops(A) do S := S union [A[i], A[j] ] ; od; od; P := combinat[powerset](S); for p in P do if IsSymmetric(p) and IsReflexive(p) and IsTransitive(p) then E := E union p; fi; od; RETURN(E); end: DetermineEqClass(1,2); DetermineEqClass(1,2,3); </pre> Como última questão a ser analisada nesta seção, vamos determinar a relação de equivalência menor que contém uma determinada relação. O elemento motivador no algoritmo é o fato de que precisamos para gerar uma relação P contendo a relação determinada R tal que P é simétrica, reflexiva e transitiva. Recordando a seção sobre fechos, deduzimos a seguinte algoritmo em pseudocódigo para determinar a relação de equivalência P contendo a relação R: 1- Criar o fecho reflexivo da relação R; chamam isso de P. 2- Criar o fecho simétrico da relação P e chamar este Q. Observe que Q ainda é reflexiva já que não há elementos foram removidos, assim que todos os pares diagonais (a,a) ainda pertencem ao Q. 3- Criar o fecho transitivo da relação Q e devolver o presente como saída. Este é reflexivo, pela mesma razão, conforme descrito na etapa anterior. Esta relação também é simétrica, já que, se (a,b) e (b,c) implica a inclusão de um elemento (a,c), em seguida, uma vez que executou o fecho simétrico, sabemos que existem elementos (c,b) e (b,a) de modo a que também temos o elemento (c,a). Daí a relação final será transitiva, reflexiva e simétrica. Nós implementamos isso em Maple como a composição dos quatro funções: '''Warshall''', '''SymmClose''', '''RefClose''', e '''MakeMatrix'''. <pre> EqContainment := Warshall @ SymmClose @ RefClose @ MakeMatrix; R2; EqContainment(R2, 1,2,3,4); </pre> == Ordenamento parcial e elementos mínimos == Nesta seção, analisamos ''posets'', ''máximos'' e ''mínimos'', assim como as ideias de supremo, ínfimo e ordenação topológica. Vamos explorar esses tópicos em Maple, e vamos deixar a exploração dos outros tópicos desta secção para o leitor. Primeiro, vamos definir um novo tipo em ''Maple'' para ordens parciais. Para esta parte, vamos considerar uma ordem parcial a ser um conjunto de pares ordenados (um objeto do tipo '''rel''') que satisfaz as três condições necessárias para uma relação a ser uma ordem parcial: reflexividade (''reflexivity''), antissimetria (''antisymmetry'') e transitividade (''transitivity''). <pre> `type/po` := proc(obj) type(obj, rel) and IsReflexive(obj) and IsAntiSymmetric(obj) and IsTransitive(obj); end: </pre> Isto ilustra uma outra maneira em que você pode definir novos tipos em Maple, deverá a álgebra de tipos estruturados revelar-se insuficiente. Em seguida, vamos construir um procedimento que determina o conjunto de elementos mínimos de um conjunto parcialmente ordenado. O procedimento a seguir usa dois argumentos: uma ordem de '''R''' parcial, e um subconjunto '''S''' do domínio de '''R'''. Ele retorna o conjunto de elementos mínimos de '''S''' com respeito à ordenamento '''R'''. <pre> MinimalElements := proc(R::po, S::set) local M, # set of minimal elements of S; returned s,t; # indices into S if S minus DomainRelation(R) &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; M := S; for s in S do for t in S minus s do if member([t,s], R) then M := M minus s; fi; od; od; RETURN(M); end: R := DividesRelation(1,2,3,6); MinimalElements(R, 1,2,3,6); MinimalElements(R, 2,3,6); </pre> Note-se que, pela dualidade, obtemos - quase de graça - uma forma muito simples implementação de elementos máximos (''MaximalElements''). <pre> MaximalElements := proc(R::po, S::set) MinimalElements(DualRelation(R), S); end: MaximalElements(R, 1,2,3,6); MaximalElements(R, 1,2,3); </pre> Em seguida, vamos construir um procedimento para calcular os elementos mínimos de um conjunto no que diz respeito a uma determinada ordem parcial, se ele existir. Nosso procedimento irá retornar o valor NULL no caso de o conjunto que tenha nenhum limite mínimo superior. Vamos realizar em várias etapas. Para fazer isso, primeiro escrevemos um procedimento que irá calcular o conjunto de todos os limites superiores de um subconjunto de um conjunto parcialmente ordenado. Este procedimento, por sua vez, se baseia na sequência de utilidade para determinar se um determinado elemento é um limite superior para um conjunto. <pre> IsUpperBound := proc(R::po, S::set, u::anything) local s; # index into S </pre> verificação de sanidade: <pre> if not member(u, DomainRelation(R)) then ERROR(`bad arguments`); fi; for s in S do if not member([s, u], R) then RETURN(false); fi; od; RETURN(true); end: UpperBounds := proc(R::po, S::set) local U, # set of upper bounds of S; returned DomR, # domain of R d; # index into DomR DomR := DomainRelation(R); </pre> verificação de erros: <pre> if S minus DomR &lt;&gt; {} then ERROR(`set must be contained in the domain of the relation`); fi; U := {}; for d in DomR do if IsUpperBound(R, S, d) then U := U union d; fi; od; RETURN(U); end: </pre> Em seguida, é conveniente introduzir um procedimento chamado '''mub''' que calcula o conjunto de limites mínimos da cota superior de um subconjunto de um conjunto parcialmente ordenado. <pre> mub := proc(R::po, S::set) MinimalElements(R, UpperBounds(R, S)); end: </pre> Agora, para completar a tarefa em mãos, precisamos apenas verificar se mub retorna uma única coisa. Se assim for, então o supremo (por definição); caso contrário, não faz, e nós devolver o valor NULL. <pre> lub := proc(R::po, S::set) local M; # set of minimal upper bounds of S M := mub(R, S); if nops(M) &lt;&gt; 1 then RETURN(NULL); fi; RETURN(op(M)); end: </pre> A ordenação topológica é usada para produzir, a partir de uma dada ordem parcial, uma ordem linear no seu domínio que é compatível com o mesmo. Por exemplo, a ordem natural no conjunto {1,2,3,6} é uma ordem linear que é compatível com a ordem parcial de divisibilidade. (de fato, isso é verdade para a rede de divisores de qualquer inteiro positivo uma vez que, se ''m'' e ''n'' são inteiros positivos, em seguida, ''m'' divide ''n'' somente se ''m'' é menor ou igual ''n''). Tendo implementado supremo e elementos mínimos, podemos agora criar um procedimento ordenação topológica que usa o algoritmo ''MinimalElements'' acima. <pre> TopSort := proc(R::po, T::set) local i, k, S, A; k := 1; S := T; A := []; while S &lt;&gt; {} do A := [op(A), MinimalElements(R, S)[1] ]; S := S minus A[k]; k := k+1; od; A; end: R := DivisorLattice(12); TopSort(R, DomainRelation(R)); R := DivisorLattice(2*3*5); TopSort(DualRelation(R), DomainRelation(R)); R := DivisorLattice(2*3*5*7); TopSort(R, numtheory[divisors](2*3*7)); </pre> == Grades == Nesta seção, vamos olhar para o problema de determinar se uma ordem parcial é reticulada. A abordagem que devemos tomar é um bom exemplo de ''top down programming'' (programação de cima para baixo). Para que possamos construir alguma exemplo interessante, vamos introduzir uma nova função para produzir exemplos de uma boa classe de reticulados. <pre> DivisorLattice := proc(n::posint) DividesRelation(numtheory[divisors](n)); end: </pre> O procedimento '''divisors''', do pacote '''numtheory''', retorna o conjunto de divisores positivos de seu argumento inteiro. Usamos o procedimento '''DividesRelation''', construído no início deste capítulo, para criar a relação de divisão neste conjunto. <pre> type(DivRel(6), po); </pre> Queremos escrever um programa em ''Maple'' que irá determinar se uma ordem parcial (finita) é um reticulado. Agora, uma ordem de ''R'' parcial é um reticulado, se, e somente se, é tanto um ''meet semilattice'' quanto um ''join semilattice''. A primeira é uma ordem parcial em que cada par de elementos tem um encontro - um limite mínimo superior ou supremo; Segundo, é aquele em que a dupla condição for satisfeita: cada par de elementos tem um supremo, ou um ínfimo. Então, o nosso teste para uma ordem parcial deve ser um reticulado, apenas precisa de verificar se estas duas condições são satisfeitas. <pre> IsLattice := proc(R::po) IsMeetSemiLattice(R) and IsJoinSemiLattice(R); end: </pre> Em seguida, usamos o fato de que uma ordem parcial é um ''meet semilattice'', se, e somente se, a sua relação dual é um ''join semilattice''. <pre> IsJoinSemiLattice := IsMeetSemiLattice @ DualRelation; </pre> Agora o verdadeiro trabalho começa; devemos codificar a função '''IsMeetSemiLattice'''. Para isso, é preciso testar se, dada a relação ''R'', cada par ''a'', ''b'' no domínio da ''R'' tem um supremo com relação a ''R''. Uma observação que simplifica a nossa tarefa consideravelmente é que, uma vez que estamos a lidar apenas com relações finitas, basta verificar que cada par tem um limite superior comum. <pre> IsMeetSemiLattice := proc(R::po) local DomR, # the domain of R r,s; # indices into R DomR := DomainRelation(R); for r in DomR do for s in DomR do if lub(R, r, s) = NULL then RETURN(false); fi; od; od; RETURN(true); end: </pre> Finalmente, todas as sub-rotinas que estão fazendo o nosso programa '''IsLattice''' estão completas, e podemos testá-lo em alguns exemplos. O seguinte resultado não deve vir com qualquer surpresa. <pre> IsLattice(DivisorLattice(24)); </pre> Mas, observe o que acontece quando nós construímos a relação ''divides'' em todos os inteiros no conjunto {1,2,3, ..., 24}. <pre> IsLattice(DividesRelation(seq(i, i=1..24))); </pre> == Relações de abrangência == É muito ineficiente, em geral, armazenar todos os pares em uma relação de ordem parcial. Há uma representação mínima de uma ordem parcial, a partir do qual toda a relação pode ser reconstruída, denominado sua relação de abrangência. (Isto não é abordado no texto, mas será útil para nós na seção seguinte, além de ser um tema importante por si só). A relação de abrangência de uma ordem parcial não é em si uma ordem parcial. É um subconjunto mínimo da ordem parcial a partir do qual todos os outros pares de relação podem ser deduzidos. Seja ''R'' uma ordem parcial em um conjunto ''S''. Dizemos que um elemento ''b'' em ''S'' abrange um elemento ''a'' em ''S'' se (''a'', ''b'') pertence a ''R'', e ''a''≠''b'', mas não há elemento ''s'' em ''S'' para o qual ambos (''a'', ''s'') e (''s'', ''b'') pertencem a ''R''. Em outras palavras, ''b'' abrange ''a'' se ''b'' é maior do que ''a'', e se não há nada entre eles. A relação de abrangência de uma ordem ''R'' parcial é a relação ''C'' de ''S'' que consiste naqueles pares (''a'', ''b'') em ''R'' para o qual ''b'' abrange ''a''. Como um exemplo simples, considere o conjunto {1,2,3,4} ordenado pela magnitude. Sua relação de abrangência é o conjunto {(1,2), (2,3), (3,4)}. Todos os outros pares, tais como (1,3), pode ser deduzida a partir da relação de abrangência, usando transitividade: 1 ≤ 2 ≤ 3, e, portanto, uma 1 ≤ 3 (isto é, o par (1,3) está na relação). Relações de abrangência também são importantes porque é a relação de abrangência de uma ordem parcial que é desenhada num diagrama de Hasse, em vez de toda a relação. Nesta seção, nós fornecemos um procedimento em Maple para calcular a relação cobrindo de uma ordem parcial. Em primeiro lugar, precisamos de um teste para saber se um determinado elemento cobre outro. <pre> Covers := proc(R::po, a, b) local u; # index into Dom(R) if a = b then RETURN(false); fi; if not member([a,b], R) then RETURN(false); fi; for u in DomainRelation(R) minus a, b do if member([a,u], R) and member([u,b], R) then RETURN(false); fi; od; RETURN(true); end: </pre> Agora podemos construir a relação cobrindo de uma ordem parcial usando o seguinte procedimento em Maple: <pre> CoveringRelation := proc(R::po) local C, # covering relation; returned DomR, # the domain of R r,s; # indices into DomR DomR := DomainRelation(R); C := {}; for r in DomR do for s in DomR do if Covers(R, r, s) then C := C union [r,s]; fi; od; od; RETURN(C); end: </pre> Vejamos alguns pequenos exemplos. <pre> CoveringRelation(DivisorLattice(6)); CoveringRelation(DividesRelation(1,3,5,7,11,13,17)); </pre> == Diagramas de Hasse == A relação de abrangência de uma ordem parcial é muitas vezes usada para representar uma ordem parcial. Nós já mencionamos que é mais eficiente (o que exige menos memória), mas também é usada para representar uma ordem parcial graficamente, no sentido de que o diagrama de Hasse é apenas uma representação visual da relação de abrangência da ordem parcial. Nós já temos a maioria das ferramentas de que precisamos para fazer uma primeira tentativa de uma representação visual de uma ordem parcial. As ferramentas de visualização no pacote '''networks''' tornam relativamente fácil de desenhar uma imagem gráfica de uma ordem parcial. Nós simplesmente calculamos sua relação de abrangência, e depois a convertemos para um gráfico e a exibimos como no seguinte procedimento. <pre> HasseDiagramFirstTry := proc(R::po) local C; C := CoveringRelation(R); networks[draw](MakeDigraph(DomainRelation(C),C)); end: </pre> Por exemplo, aqui está uma imagem do divisor reticulado de 2100. <pre> HasseDiagramFirstTry(DivisorLattice(2*3*5*7)); </pre> Infelizmente, esta sofre da desvantagem de não desenhar diagramas Hasse na forma tradicional, com o fim de os elementos representados pelo fluxo do diagrama. Para corrigir isso, precisamos fazer um pouco de codificação. A ideia é organizar os elementos de um ordenado parcialmente definida em níveis, e então usar a opção '''Linear''' para rotina '''draw''' para formar o diagrama mais apropriadamente. Várias rotinas de serviços públicos são necessárias. Esta função é usada para verificar se um elemento é um átomo; isto é, que não tem precedentes. Destina-se a ser utilizado apenas com relações de abrangência '''CR'''. O argumento extra '''D''' é necessário para que possamos localizá-lo mais tarde. <pre> IsAtom := proc(CR::rel, D::set, a::anything) local d; for d in D do if member([d,a], CR) then </pre> encontrou um predecessor: <pre> RETURN(false); fi; od; </pre> deve ser um átomo: <pre> RETURN(true); end: </pre> Nós usamos isso no próximo procedimento, que determina todos os átomos em um determinado subconjunto de uma relação de cobertura. <pre> Atoms := proc(CR::rel, D::set) local A, # set of atoms; returned d; # index into D A := {}; for d in D do if IsAtom(CR, D, d) then A := A union d; fi; od; RETURN([op(A)]); end: </pre> Aqui é a nossa nova implementação do Diagrama de Hasse. A maior parte do novo trabalho envolve a disposição dos elementos do conjunto parcialmente ordenado em uma sequência de níveis para passar para '''Linear''' na rotina '''draw'''. <pre> HasseDiagram := proc(R::po) local L, C, G, A, D; C := CoveringRelation(R); D := DomainRelation(C); # = DomainRelation(R) G := MakeDigraph(D, R); L := NULL; while D &lt;&gt; {} do A := Atoms(C, D); L := L, sort(A); D := D minus op(A); od; networks[draw](Linear(L), G); end: </pre> Ela produz imagens muito melhores, com o fluxo, da esquerda para a direita, seguindo aproximadamente os elementos crescentes do conjunto parcialmente ordenado. <pre> HasseDiagram(DivisorLattice(4)); HasseDiagram(DivisorLattice(6)); HasseDiagram(DivisorLattice(81)); </pre> Os dois seguintes são especialmente bonitos! <pre> HasseDiagram(DivisorLattice(2*3*5*7)); HasseDiagram(DivisorLattice(2*3*5*2)); </pre> == Cálculos e explorações == Nesta seção, vamos explorar como o ''Maple'' pode ser usado para resolver as questões 1, 4 e 5 da secção cálculos e explorações. 1- Exibir todas as diferentes relações em um conjunto com 4 elementos. '''Solução:''' Como de costume, Maple é demasiado poderoso para resolver somente uma única instância do problema geral sugerido por esta questão. Nós fornecemos aqui um procedimento muito simples que irá calcular todas as relações em qualquer conjunto finito. <pre> AllRelations := proc(S::set) local s, t, # indices into S C; # Cartesian product SxS C := {}; for s in S do for t in S do C := C union [s,t]; od; od; combinat[powerset](C); end: </pre> Nós agora testar o nosso procedimento em um conjunto menor, de modo a manter a saída de um comprimento razoável. O leitor é convidado para determinar o tempo de execução e o comprimento de saída para o processo, quando o conjunto de entrada tem cardinalidade 4 ou 5. Tenha em mente que existem <math>2^{n^{2}}</math> relações em um conjunto com n membros. <pre> AllRelations(1,2); </pre> 2- Determine quantas relações transitivo existem em um conjunto com ''n'' elementos para todos os inteiros positivos ''n'' com ''n'' ≤ 7. '''Solução:''' Vamos construir cada possíveis <math>N x N</math> da matriz zero-um, usando um algoritmo semelhante ao de contagem binária. O pseudocódigo é: 1- Para cada valor de 1 até <math>2^{(n ^{2})}</math>, criamos uma lista que é a representação de base 2 desse inteiro. 2- Nós criamos uma matriz M com elementos sendo a lista de valores. Estes são todos os possíveis <math>n^{2}</math> de matriz zero-um (o leitor pode provar esta afirmação). 3- Avaliar o fechamento transitivo de cada uma das matrizes que criamos, e retornar o conjunto de fechos transitivos dessas matrizes. A implementação é como se segue: <pre> FindTransitive := proc(S::set) local i, j, T, P; P := {}; for i from 0 to 2^(nops(S)^2)-1 do T := convert(i, base, 2); while nops(T) &lt; nops(S)^2 do T := [op(T), 0]; od; P := P union matrix(nops(S), nops(S), T); od; P; end: </pre> Mais uma vez, utilizamos o nosso processo em valores relativamente pequenos (devido ao comprimento da saída), e deixamos a exploração adicional para o leitor. <pre> P := FindTransitive(1,2): Q := {}: for i from 1 to nops(P) do Q := Q union Warshall(P[i]): od: Q; </pre> 3- Encontre o fecho transitivo de uma relação em um conjunto com pelo menos 200 elementos. '''Solução:''' Vamos gerar aleatoriamente uma matriz zero-um com dimensão 10x10, e em seguida, aplicar o algoritmo de \textbf{Warshall} para deduzir o fecho transitivo matriz. Para gerar uma matriz zero-um aleatória, usamos a função '''randmatrix''' do pacote '''linalg''', e fornecer seu terceiro argumento, opcional com um procedimento que gera uma sequência aleatória 0's (zero) e 1's (uns). Em seguida, aplicamos o algoritmo de '''Warshall''' a esta matriz aleatória, obtendo o resultado. Aqui, para economizar espaço, podemos aplicar este procedimento para uma matriz de 10x10 como um exemplo. <pre> Q := randmatrix(10, 10, entries=rand(2)); Warshall(Q); </pre> == Referências == [http://www.mhhe.com/math/advmath/rosen/r5/student/ch07/maple.html Maple: Chapter 7. Relations] 7f768e6426293f87587da6c6dc02d7b4b6c22375 Indução e Recursão Matemática 0 128 771 754 2016-05-31T13:50:33Z Gellyviana 31 /* Extra */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 911. 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 0ac2705bc71df70022165c9628efe7054c140298 785 771 2016-06-18T01:35:22Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 91. <math> McCarthy := proc (n::integer) option remenber; if 100 < n then return n-10; else return McCarthy(McCarthy(n+11)); end if; end proc; </math> 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. f7befd0434031f385984d5246757b41c07df6114 786 785 2016-06-18T01:35:47Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 91. <math> McCarthy := proc (n::integer) option remenber; \\ if 100 < n then return n-10; \\ else return McCarthy(McCarthy(n+11));\\ end if;\\ end proc;\\ </math> 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 4dc22a1b88486c9e415fd83288458ab9887c9736 787 786 2016-06-18T01:36:51Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 91. ''''' McCarthy := proc (n::integer) option remenber; if 100 < n then return n-10; else return McCarthy(McCarthy(n+11)); end if; end proc; ''' '' 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 54888fc0ccbd5b09ddf8e8e2286340099782ec52 788 787 2016-06-18T01:37:41Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 91. == McCarthy := proc (n::integer) option remenber; if 100 < n then return n-10; else return McCarthy(McCarthy(n+11)); end if; end proc; == 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 9852e21342a772173a1dbe9c1a795e16b0cca37b 790 788 2016-06-18T01:40:39Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 91. [[File:q1.jpg]] 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. a3f70e39c26732d570ee66a35cbb037a4563545f 792 790 2016-06-18T02:37:24Z Marcielmanoel15 30 /* Exercícios e Projetos */ wikitext text/x-wiki Este projeto descreve o uso da linguagem Maple para desenvolvimento da Indução Matemática e Recursão na disciplina de Fundamentos da Matemática para Computação II. Com uso da tradução livre do documento que foi disponibilizado do livro Matemática Discreta do Keneth H. Rosen, descreveremos como a Indução pode ser facilmente entendida e utilizada com Maple . ==Introdução== Neste capítulo descreveremos como o maple pode ser usado para ajudar na compreensão e construção de provas matemáticas. Capacidades computacionais podem não parecer particularmente relevantes para o estudo das provas, embora na realidade essas capacidades possam ser úteis em provas de várias maneiras. Neste capítulo, descrevemos como o maple pode ser útil para trabalhar com regras formais de inferência, descrevemos como pode ajudar a compreender provas construtivas e não construtivas. Além disso, mostramos como usar o maple para ajudar a desenvolver provas usando indução matemática, até mesmo mostrando sua utilidade para ambos o passo base e passo indutivo, na prova da fórmula de somatório. Ademais, mostraremos como o maple pode ser usado para computar termos de sequencias definidas recursivamente. Vamos também comparar a eficiência da geração de termos dessa sequencia via técnicas indutivas versus técnicas recursivas. ==Método de prova== Embora o maple não possa receber teoremas e resultados de provas para esses teoremas, pode receber expressões lógicas e simplificadas ou determinar características tais como: se uma expressão booleana pode ser satisfeita ou se é uma tautologia. Para trabalhar com expressões lógicas no maple, precisamos usar alguns dos recursos oferecidos pelo pacote de '''logic''' (um assunto abordado de maneira mais aprofundada no capítulo 9). Primeiramente examinaríamos os operadores lógicos: conjunção, disjunção, negação e implicação. Não existe (no Maple). Para estudar as condicionais, devemos trabalhar com os operadores booleanos inativos oferecidos pelo pacote '''logic'''. Todos esses são iniciados com o caracter '''mexpr''' <math>\&</math> , por exemplo: usamos <math>\&and</math> invés de <math>and</math> e <math>\&not</math> invés de <math>not</math>. Em seguida, vão alguns exemplos do uso de operadores booleanos inativos: [[File:imagem1.png]] Nos preocupamos agora em determinar como o maple simplifica expressões booleanas caso estejam combinadas. Começamos com um simples exemplo de dupla negação: [[File:capeta.png]] Isto pode ser simplificado através do uso da função '''bsimp''' do maple. [[File:imagem3.png]] Agora, seguimos para um exemplo mais complexo, no qual o leitor pode confirmar a corretude da simplificação, construindo uma tabela verdade. [[File:imagem4.png]] O próximo exemplo ilustra a simplificação do Modus Ponens. Primeiro afirmamos que p implica q e p é verdadeiro. [[File:imagem5.png]] Então simplificamos a expressão booleana, [[File:imagem6.png]] Determinando que q e p são verdadeiros, como nós já sabíamos que p era verdadeiro, nós concluímos que q é verdadeiro. A função '''bsimp''' é um simplificador geral para expressões booleanas construídas usando os operadores booleanos inativos. A função computa uma expressão booleana simplificada equivalente ao seu argumento. Podemos também usar o maple para determinar se uma expressão é uma tautologia através do uso da função tautology oferecida pelo pacote logic. [[File:imagem7.png]] Agora mostramos como usar o maple para compreender algumas provas construtivas. Especificamente, vamos examinar como explorar a construção de uma lista sequencial de números compostos. [[File:imagem8.png]] Enquanto o maple pode ser usado para gerar uma lista de n inteiros compostos consecutivos, gerados por prova, não é possível derivar a prova em si usando o maple. Devemos observar que este argumento não fornece o menor conjunto de n inteiros compostos consecutivos. Embora dado um inteiro positivo n, é possível usar o maple para encontrar a menor sequencia de n inteiros compostos consecutivos. Através do maple abordaremos a prova não construtiva da existência de um número infinito de números primos. Por ser uma prova não construtiva, não podemos simplesmente criar um algoritmo para gerar um número primo maior, assumindo a existência de um número primo máximo. Embora a ideia chave da prova seja considerar a primalidade do inteiro <math>N!+1</math>, é possível que <math>N!+1</math> seja por si só um número primo, porém mesmo que não seja seu maior fator primo deve ser maior que n. É possível encontrar o menor fator primo fatorando <math>N!+1</math> diretamente, usando a rotina '''ifactor''' da biblioteca maple. [[File:imagem9.png]] Podemos observar pelo resultado que, enquanto alguns desses números são primos, outros não são, a partir disso, podemos fazer a leitura do menor fator primo. Para determinar o menor fator primo de cada um desses inteiros, podemos escrever a seguinte rotina: [[File:imagem10.png]] Ela usa o procedimento '''factorset''' do pacote '''numtheory''' para computar o conjunto de fatores do inteiro de entrada, e então simplesmente seleciona seu menor membro. [[File:imagem11.png]] Agora confrontamos nosso exemplo final do uso do maple explorando teoremas matemáticos. Neste caso vamos explorar a conjectura de Goldbach: que é, todo inteiro par maior que 4 pode ser expressado como a soma de dois primos. [[File:imagem12.png]] Agora, criamos um procedimento para examinar o Goldbach a conjectura mais automaticamente. [[File:imagem13.png]] ==Indução Matemática== O maple pode ser usado para auxiliar na elaboração de provas de várias afirmações matemáticas usando a indução matemática. De fato, com o maple como seu assistente, você pode conduzir inteiramente o processo de descoberta e averiguação de forma intuitiva. É provável que entre os primeiros exemplos de indução matemática, encontremos a verificação da fórmula <math>1+2+3+...+n = n(n+1)/2</math> ,a soma dos primeiros n inteiros positivos. O maple se adequa para provar fórmulas como essa porque os passos envolvidos numa prova indutiva incluem manipulação simbólica. É possível gerar uma grande quantidade de dados numéricos para serem examinado. [[File:imagem14.png]] Através da geração de um grande conjunto de dados numéricos de pouca compreensão, eventualmente será possível determinar a fórmula acima. A saída mostra que <math>n^2</math> é um pouco menos que o dobro da soma dos n primeiros inteiros para os valores de n testados. Partindo de um padrão, é possível perceber que a fórmula correta é uma função quadrática de n, resolva para encontrar os coeficientes e então teste se esse procedimento produz a fórmula correta. Uma técnica útil para experimentos semelhantes é gerar listas de pares que consista da sequencia que esteja interessado e de várias possibilidades que você elabore. Para investigar a hipótese de que a fórmula seja quadrática, devemos começar gerando uma lista de pares semelhantes ao seguinte: [[File:imagem15.png]] Para explorar se a soma é uma função quadrática de n, podemos inserir um quadrático genérico em n e solucionar para encontrar os coeficientes a,b e c: [[File:imagem16.png]] Precisamos de três equações para serem solucionadas em busca dos três coeficientes: [[File:imagem17.png]] Agora, instruímos o maple para resolver essas equações e encontrar os três coeficientes. [[File:imagem18.png]] A nossa fórmula original torna-se: [[File:imagem19.png]] Neste ponto, as habilidades do maple permitem manipular expressões simbolicamente para ajudar a construir uma prova indutiva. Segue um exemplo de como a prova interativa da fórmula acima pode ser conduzido no maple por indução matemática. O termo geral da soma é: [[File:imagem20.png]] Enquanto o lado direito da fórmula é: [[File:imagem21.png]] Podemos usar o procedimento '''subs''' para verificar o passo base da indução; neste caso o passo base é <math>n=1</math> [[File:imagem22.png]] Os resultados coincidem (concordam), então o passo base é estabelecido. Para o passo indutivo, usamos que a fórmula seja válida para <math>n=k</math>. [[File:imagem23.png]] Para somar k+1 termos, computamos: [[File:imagem.png]] Por fim, a fórmula para n=k+1 é: [[File:imagem24.png]] Os resultados coincidem, então o passo indutivo é verificado. A fórmula agora segue por indução matemática. Assim, podemos concluir que enquanto o maple ainda não é capaz de construir provas inteiramente por conta própria, é uma ferramenta muito efetiva para ser usada na construção de provas interativas. Agora, vamos considerar um exemplo mais complicado. A fórmula do somatório. <math>S = 1.1! + 2.2! +...+n.n!</math> É bem menos óbvio que o exemplo anterior. Para descobri-lo, iniciaremos gerando alguns dados numéricos. [[File:imagem25.png]] Se um padrão não é imediatamente óbvio,podemos auxiliar nossa intuição gerando uma sequencia paralela. [[File:imagem26.png]] Observando isto um pouco, fica óbvio que estamos no caminho certo, vamos apenas fazer alguns pequeno ajustes. [[File:imagens.png]] Desta evidencia, devemos inferir a conjectura que a fórmula para nosso somatório é: <math>S=(n+1)!-1</math> A prova indutiva pode ser conduzida assim como foi feito no primeiro exemplo. [[File:imagem28.png]] O passo base é: [[File:entrenseind.png]] O passo indutivo é: [[File:imagem29.png]] Usando um pouco de manipulação algébrica, vemos que as duas últimas fórmulas são iguais. Isso completa a prova via indução matemática. Concluímos que nosso palpite sobre a fórmula está correto. ==Definições Recursiva e Interativa== As funções do maple podem ser definidas tanto processualmente (usando a função proc) como explicitamente (usando a notação ->), cada um desses métodos envolve meios interativos e recursivos de definição. Iniciamos nosso estudo, usando a função -> do maple. Se nós quiséssemos definir uma função polinomial <math>A(n)= 3n^3 + 41n^2- 3n + 101</math> nós usaríamos o seguinte comando: [[File:imagem30.png]] Agora, se quiséssemos definir uma função recursivamente, digamos: <math>b(n) =b(n-1)^2 + 2b(n-1) +6</math>, com a condição inicial <math>b(0)=2</math>, então informaríamos (inserir): [[File:imagem31.png]] Se quiséssemos ver uma sequencia de valores para a função b, podemos usar a função seq, para exibir as saídas de um dado intervalo de entradas. [[File:imagem32.png]] Agora, criaremos uma função similar a '''b''', chamada '''f1''', que encontrará os números Fibonacci. [[File:imagem33.png]] Enquanto a notação <math>-></math> para funções é conveniente e intuitiva, ela não oferece todas as facilidades para melhoria da eficiência que estão disponíveis no uso do comando '''proc'''. Para forçar o maple a calcular esses valores de forma eficiente, usamos a opção '''remember''' para definições de procedimentos afetados pelo uso do '''proc'''. Esta opção exige que o maple lembre de qualquer valor para procedimento que já tenha sido computado através do armazenamento destes em uma tabela Fibonacci. [[File:imagem34.png]] Esse método processual engloba ambos os casos, o caso base (quando <math>n <=2</math>) e os casos indutivos (como na condição '''else'''). Adicionalmente, o procedimento tem a indicação da função remember, forçando o maple a manter controle sobre quais valores da função já foram encontrados, para que estes sejam diretamente verificados ao invés de serem novamente computados. [[File:imagem35.png]] Agora, para ilustrar a diferença em complexidade computacional, compararemos os métodos processual e “->” usando a função '''time''' do maple. [[File:imagem36.png]] Então , fica claro que a função '''remember''' pode fazer uma enorme diferença em complexidade de tempo. Outra maneira de melhorar a eficiência de uma função definida recursivamente é reescrevê-la para evitar o uso de recursão. Ao invés disso, reestruturamos para que use um algoritmo iterativo. Na construção de um algoritmo iterativo, os componentes chave consistem em criar uma forma de loop (um '''for''' ou '''while''') que computará valores começando do menor para o maior. Este método de programação é chamado de '''bottom up''': onde os menores valores de uma sequencia são computadose então usados para valores maiores. [[File:imagem37.png]] Faça um contraste entre esse procedimento e o procedimento recursivo '''f2''' que foi definido anteriormente. [[File:imagem38.png]] Ambos os casos base e passo recursivo são explicitamente definidos no corpo do procedimento. O algoritmo primeiro tenta computar diretamente o valor verdadeiro e pede valores de subcasos conforme é solicitado. Este método de programação é conhecido como top-down, pois os valores maiores são computados através da quebra da entrada em partes menores e da combinação dos resultados. Similar a atravessar uma árvore binária. Perceba que o procedimento recursivo com a opção remember e o procedimento iterativo tem praticamente a mesma performance. Para os primeiros vinte números Fibonacci, obtemos: [[File:imagem39.png]] Que é comparável às vezes obtidas para '''f2'''. Note que a implementação puramente recursiva '''f1''' não possibilita o uso para f2(100). De fato, um bom exercício é mostrar que para fazê-lo, '''f2''' precisaria de aproximadamente [[File:imagem40.png]] Chamadas para operar todos os subcasos que surgirem. Mesmo a um bilhão de subcasos por segundo, isso exigiria mais de seis mil anos para sua conclusão. ==Computações e Explorações== Nesta seção do material, exploraremos o modo como o maple pode ser usado para resolver as questões 4,5 e 8 da seção “computações e explorações” do livro. 1 - Quantos pares de números primos podem ser encontrados? Para determinar quantos pares de números primos existem, usaremos o pacote “numtheory” do maple, que contém as funções '''nextprime''', '''prevprime''' e '''ithprime''' [[File:imagem41.png]] Agora, após formada uma lista de primos, queremos extrair quaisquer pares de primos que ocorram nessa lista. [[File:imagem42.png]] Ao invés de dar como resultados os pares de números primos, o número de sequências de primos pode indicar um padrão, então construímos uma lista dos '''i'''’s que formarem pares. [[File:imagem43.png]] Parece não haver um padrão óbvio ocorrendo. 2- Determine quais números Fibonacci são divisíveis por 5, quais são divisíveis por 7 e quais são divisíveis por 111. Prove que suas conjecturas estão corretas. Primeiro vamos gerar dados para trabalhá-los (manipulá-los). [[File:imagem44.png]] Queremos determinar os índices n para os quais o enésimo número Fibonacci é divisível por 5. Uma maneira de fazer isso é construindo uma lista, através de testes com os dados acima e adicionando à lista somente os índices n para os quais o teste retorne '''verdadeiro'''. [[File:imagem45.png]] Isso constrói uma lista indicando quais entre os primeiros 500 números Fibonacci são múltiplos de 5. Os dados indicam que o enésimo número Fibonacci <math>F_n</math> é divisível por 5, somente se n é divisível por 5. Para obter evidências para a conversão, devemos testar se <math>F_{5n}</math> é divisível por 5, para tantos n quanto forem possíveis. Para que nosso teste seja conciso e ainda permita testar um grande intervalo(série) de valores, vamos implementá-lo de maneira que nenhum resultado seja produzido, a menos que seja encontrado um contra exemplo. [[File:imagem46.png]] Assim, não há contra exemplo entre os primeiros 5000 números Fibonacci. Você pode testar com valores maiores que 1000, para obter novas evidências. Outra abordagem ligeiramente diferente pode ser usada para encontrar os números Fibonacci divisíveis por um dado inteiro, neste caso, o número 7. Nós simplesmente construímos o teste de divisibilidade no comando para gerar dados. [[File:imagem47.png]] Podemos agora selecionar os índices dos pares cujo segundo membro seja igual a 0. [[File:imagem48.png]] Podemos perceber um padrão nesses dados, como o seguinte: [[File:imagem49.png]] Você pode tentar averiguar se esse padrão persiste, substituindo 500 na definição de '''fib_list''' por números muito maiores. (O teste da divisibilidade por 111, nós deixamos pra você). 2 – A notória conjectura <math>3x + 1</math> (também conhecida como conjectura de Collatz e por vários outros nomes) afirma que: independente de qual inteiro x você escolha para iniciar, em iteração com a função f(x), onde f(x) = x/2, se x é par e f(x) = 3x+1 se x é ímpar, sempre produz o inteiro 1. Cheque essa conjectura para tantos inteiros positivos possíveis. Para inicar, precisamos definir a função, a qual examinaremos. [[File:imagem50.png]] Agora escrevemos uma função que fará a iteração da função Collatz até que o valor obtido seja igual a 1, nós incluímos uma variável “count” por dois motivos: Primeiro, queremos ter uma idéia de quanto tempo leva para que as iterações estabilizem; Segundo, já que não sabemos ao certo se as iterações vão estabilizar para um dado valor de entrada “seed”, nos codificamos um limite superior para o número de iterações a serem computadas. [[File:imagem51.png]] Para averiguar a conjectura para os 1000 primeiros inteiros, podemos usar a função '''IC''' como no exemplo a seguir: [[File:imagem52.png]] Perceba que, o fato de a função ter eventualmente parado é a averiguação que buscamos. ===Exercícios e Projetos=== 1.Use Maple para encontrar e provar a fórmula do soma do primeiros <math>k^N</math> ('''K''' elevado a enésima potência) inteiros positivos para '''N''' = 4, 5, 6, 7, 8, 9 e 10. 2.Use Maple para estudar a função McCarthy 91. [[File:q1.jpg]] 3.Escreva um procedimento no Maple para encontrar a menr (isto é, a primeira) sequência consecutiva de N inteiros positivos compostos, para um inteiro N positivo arbitrário. 4.Use Maple para desenvolver um procedimento para gerar números Ulam. Faça e estude numericamente as conjecturas sobre a distribuição desses números. 5.Escreva um procedimento no Maple que receba um inteiro K como entrada e determine se é ou não o produto dos primeiros K primos mais 1, e se é primo ou não, através da fatoração deste número. [[File:q2.jpg]] 6.Outra maneira de mostrar que existem infinitos primos é assumir que existem apenas N primos <math>p_1, p_2, ..., p_n</math>. Mas isso é uma contradição já que <math>p_1, p_2, ..., p_{n+1}</math> tem ao menos um fator primo que não é divisível por <math>p_i</math>, i = 1, 2, ..., N. Encontre o menor fator primo de <math>2, 3, ..., p_{n+1}</math> para todos os inteiros positivos N que não excedam 200. Para os quais N é este número primo. 7.O número Lucas satisfaz a recorrência <math>L_{n} = L{n-1} + L_{n-2}</math> e as condições iniciais <math>L_0 = 2</math> e <math>L_1 = 1</math>. Use o Maple para obter evidências para as conjecturas sobre a divisibilidade dos números Lucas por outros divisores inteiros diferentes. 8.A sequência <math>a_1, a_2, a_3, ...</math> é chamada de periódica se existirem inteiros positivos N e p, para os quais <math>a_n = a_{n+p}</math>, para todo <math>n \ge N</math>. O menor inteiro p, para o qual isso é verdadeiro é chamado de período da sequência <math>a_1, a_2, a_3, ...</math> se diz que é o módulo periódico m, para um inteiro positivo M, se a sequência <math>a_1~ mod~ m</math> ,<math> a_2~ mod~ m</math>, <math>a_3~ mod~ m</math>, ... é periódica. Use maple para determinar se a sequência Fibonacci é modulo periódico m, para vários inteiros m e, se for, encontre o período.Você pode, através da inspeção de valores de m diferentes o suficiente, fazer alguma conjectura a respeito da relação entre m e o período? Faça o mesmo para sequências que julgar interessante. ==Extra== Nesta sessão serão tratados algumas demonstrações sobre indução e recursão contidas na pagina 240, cujo Extra 1, 2, 3 e 4 são implentações na linguagem de C++ como forma de uma abordagem alternativa como sugestão do Professor Umberto Rivieccio da disciplina de FMC II. *Exemplo 1 Use o princípio da indução matemática para provar que <math>1 + 5 + 5^2 +5^3 +...+5n = \frac {5^{n+1}-1}{4} </math> para todo <math> n \ge 0</math> Seja p(n) a afirmação <math>1+5^2+5^3+...+5n = \frac {5^{n+1} -1}{4}</math> '''Passo base:''' p(0) : <math>1= \frac {5^{0+1}-1}{4}</math>, (perceba que a soma no lado esquerdo de p(0) inicia e termina com o primeiro termo 1, consequentemente é apenas o primeiro termo 1). <math>p(0)</math> é verdadeiro pois ambos os lados são iguais a 1. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>: suponha para qualquer k, <math>p(k)</math> é verdadeiro, isto é, <math>1+5+5^2+5^3+...+ 5k = \frac {5^{k+1} -1}{4}</math> Precisamos mostrar que a próxima afirmação <math>p(k+1)</math>, é verdadeira: <math>1+5+5^2 +5^3 +...+5^{k+1} = \frac {5^{k+2} -1}{4}</math> Para fazer isso, iniciamos com <math>p(k)</math> e adicionamos o próximo termo, <math>5^{k+1}</math>, em ambos os lados, depois mostramos que essa é a afirmação <math>p(k+1)</math>. <math>1+5^2+5^3+...+5k = \frac {5^{k+1} -1}{4}</math> <math>+5^{k+1} -------------- 5^{k+1}</math> <math>1+5^2+5^3+...+5k+5^{k+1} = \frac {5^{k+1} -1 +4.5^{k+1}}{4}</math> <math>=\frac {(1+4)5^{k+1} -1}{4} </math> <math>=\frac {5.5^{k+1} -1}{4}</math> <math>= \frac {5^{k+2} -1}{4}</math> Isto é, <math>p(k+1)</math>. Portanto uma afirmação verdadeira p(k, é seguida por outra afirmação verdadeira <math>p(k+1)</math>, por isso <math>p(k) \to p(k+1)</math> é verdadeira. Assim, pelo princípio da indução matemática ,<math> p(n)</math> é verdadeiro para todo <math>n\ge 0</math>. Nota: Alternativamente, a prova de <math>p(k) \to p(k+1)</math> pode ser escrita dessa forma. Começamos escrevendo o lado esquerdo da equação que é <math>p(k+1)</math>. Então mostramos que pode ser reescrita para dar o lado direito de <math>p(k+1)</math>. Perceba que a suposição que <math>p(k)</math> é verdadeira está sendo usada em substituição do primeiro passo. <math>+5+5^2+5^3 + ...+ 5k +5^{k+1} = \frac {5^{k+1} -1}{4} + 5^{k+1}</math> <math> = \frac {5^{k+1} -1} + 4.5^{k+1}{4}</math> <math>= \frac {(1+4).5^{k+1} -1}{4}</math> <math>=\frac {5.5^{k+1}-1}{4}</math> <math>=\frac {5^{k+2}-1}{4}</math> *Exemplo 2 Prove que : <math>\sum_{k=1}^n (2k+3)= n(n+4)</math> para todo <math>n\ge 1</math> Seja <math>p(n)= 5+7+9+...+(2n+3)= n.(n+4)</math> '''Passo base:''' <math>p(1)</math> afirma que <math>2.1+3=1(1+4)</math>, é verdadeiro, já que ambos os lados são iguais a 5. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro, isto é: <math>5+7+9+...+(2k+3 )=k(k+4)</math> Adicionamos <math>(2k+3)</math> a ambos os lados e simplificamos : <math>5+7+9+...+(2k+3)+(2k+5) = k.(k+4)+(2k+5)</math> <math>= k^2 + 6k + 5</math> <math>= (k+1).(k+5)</math> Que é <math>p(k+1)</math>, assim, <math>p(k) \to p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática,<math> 5+7+9+...+(2n+3)= n(n+4)</math> é verdadeiro para todo <math>n \ge 1</math>. *Exemplo 3 Encontre uma fórmula para <math>(1 -\frac {1}{2^2}) (1- \frac {1}{3^2}) (1 - \frac{1}{4^2})...(1 -\frac {1}{n^2})</math> Para <math>n \ge 2</math>, use o princípio de indução matemática para provar que sua fórmula está correta. Primeiro precisamos de um palpite para a fórmula do produto. Usando <math>n= 2,3,4,5</math> obtemos: <math>(1-\frac {1}{2^2})</math> = <math>\frac {3}{4}</math> <math>(1-\frac {1}{2^2}) (1-\frac {1}{3^2})=</math> <math> \frac {3}{4} . \frac {8}{9}</math> =<math> \frac {2}{3}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) =</math> <math>\frac {3}{4} . \frac{8}{9} . \frac{15}{16}</math> =<math> \frac {5}{8}</math> <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) (1-\frac{1}{5^2}) = </math><math> \frac {3}{4} . \frac {8}{9} . \frac {15}{16} . \frac {24}{25} =</math> <math>\frac {3}{5}</math> Assim, os produtos são <math>\frac {3}{4}</math>, <math>\frac {2}{3}</math>,<math> {5}{8}</math>,<math> {3}{5}</math>. Podemos reescrever essas frações como <math>\frac {3}{4}</math>, <math>\frac {4}{6}</math>, <math>\frac {5}{8}</math>, <math>\frac {6}{10}</math>. Isso sugere <math>\frac {n+1}{2n}</math> como a forma geral da soma. Seja <math>p(n) : (1-\frac {1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> =<math>\frac {n+1}{2n}</math> Agora, tentamos mostrar que <math>p(n)</math> é verdadeiro para todo <math>n \ge 2</math>. '''Passo base:''' <math>p(2)</math> é verdadeiro. <math>p(2)</math> afirma que (1-1/2²) = <math>\frac {2+1}{2.2}</math>, que é verdade pois, ambos os lados são iguais a <math>\frac {3}{4}</math>. '''Passo indutivo:''' <math>p(k) \to p(k+1)</math>: suponha que <math>p(k)</math> é verdadeiro para algum k. Portanto: <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{k^2}) </math>= <math>\frac {k+1}{2k}</math> Multiplica-se ambos os lados da equação por <math>(1-\frac{1}{(k+1)^2})</math> para obter <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2}) ...(1-\frac{1}{k^2}) (1-\frac{1}{(k+1)^2})</math> = <math>\frac {k+1}{2k} (1-\frac{1}{(k+1)^2})</math> <math>=\frac {k+1}{2k} (\frac{(k+1)^2}- 1 {(k+1)^2})</math> <math>=\frac {k+1}{2k} .\frac {k(k+2)}{(k+1)^2}</math> <math>= \frac {k+2}{2(k+1)}</math> Que é <math>p(k+1)</math>. Portanto, pelo princípio da indução matemática, <math>(1-\frac{1}{2^2}) (1-\frac{1}{3^2}) (1-\frac{1}{4^2})...(1-\frac{1}{n^2})</math> = <math>\frac {n+1}{2n}</math> é verdadeiro para todo <math>n \ge 2</math>. *Exemplo 4 Use o princípio da indução matemática para provar que <math>2 \mid (n^2-n)</math> para todo <math>n \ge 0</math>. Seja <math>p(n)</math> a afirmação <math>2 \mid (n^2-n)</math>. '''Passo base:''' <math>p(0):</math> é verdadeiro, pois <math>2 \mid (0^2-0)</math>, ou <math>2 \mid 0</math>. '''Passo indutivo:''' <math>p(k)\to p(k+1):</math> suponha <math>p(k)</math> verdadeiro para algum inteiro não negativo k, isto é, <math>2 \mid(k^2-k)</math>. Precisamos mostrar que <math>p(k+1)</math> é verdadeiro: <math>2 \mid (k+1)^2 - (k+1)</math>, mas <math>(k+1)^2 - (k+1)= k^2 +2k + 1 - k - 1 = (k^2 - k) + 2k</math> .Porém, <math>2 \mid (k^2-k)</math> através de <math>p(k)</math>, e <math>2 \mid 2k</math>, já que <math>2k</math> é par. Portanto , 2 é divisor da diferença, isto é, <math>2 \mid (k+1)^2 - (k+1)</math>. Assim, <math>p(k+1)</math> é verdadeiro. Pelo princípio da indução matemática , <math>2\mid (n^2-n)</math> é verdadeiro para todo <math>n \ge 0</math>. *Exemplo 5 Use o princípio da indução matemática para provar que: <math>n^2 - 5n + 3 > 0 </math>, para todo <math>n >=5</math>. Seja p(n) a afirmação <math>n^2 - 5n + 3 > 0</math>. '''Passo base:''' O passo base é <math>p(5)</math>, <math>p(5)</math> afirma que <math>5^2 - 5.5 + 3 > 0</math>, o que é verdade, pois <math>3 > 0</math>. '''Passo indutivo:''' <math>P(k) \to p(k+1)</math>, suponha que <math>k^2 - 5k +3 > 0</math> para algum inteiro <math>k \ge 5</math>. Precisamos mostrar que <math>(k+1)^2 - 5(k+1) + 3 > 0</math> é verdadeiro. Mas <math>(k+1)^2 - 5(k+1) + 3 = k^2 + 2k + 1 - 5k - 5 + 3 = (k^2- 5k + 3) + (2k - 4)</math>. O termo <math>k^2 - 5k + 3</math> é positivo por <math>p(k)</math>, e <math>2k - 4</math> é positivo, pois <math>k</math> é no mínimo <math>5</math>, daí a soma é positiva e <math>p(k+1)</math> é verdadeiro. Portanto, pelo princípio da indução matemática, <math>n^2 - 5n + 3 > 0</math> é verdadeiro para todo <math>n \ge 5</math>. *Exemplo 6 A) Escreva um algoritmo recursivo para encontrar a soma dos n primeiros inteiros positivos pares. B) Use a indução matemática para provar que o algoritmo está correto. Solução: A)Seja evensum(n) a soma dos n primeiros inteiros positivos. O algoritmo recursivo é: Procedure evensum(n : integer >= 1) If n=1 then evensum(n): = 2 Else evensum(n):= evensum(n-1) +2n B)Seja <math>p(n)</math>, evensum(n) (a soma dos n primeiros inteiros positivos pares). ‘’’Passo base:’’’ Quando <math>n = 1</math>, a ação then (então) do procedimento toma efeito e atribui evensum(1) = 2, que é a soma do primeiro inteiro par. ‘’’Passo indutivo:’’’ Assumimos <math>p(k)</math> verdadeiro para algum <math>k \ge 1</math> e devemos mostrar que <math>p(k+1)</math> é também verdadeiro. A proposição <math>p(k)</math> afirma que evensum(k) é a soma dos primeiros <math>k</math> inteiros positivos pares. De acordo com o algoritmo, como <math> k+1</math>, a condição else é usada (com <math>k+1</math> em lugar de<math> n</math>) para obter evensum(k+1) e retorna: evensum(k+1) = evensum(k) <math>+ 2(k+1)=</math> soma dos primeiros k inteiros pares <math>+ 2(k+1)</math> Que é a soma dos primeiros <math>k+1</math> inteiros pares. Portanto, o passo de indução é seguido. O princípio da indução matemática prova que o algoritmo está correto. *Exemplo 5 Para a sequencia de números Fibonacci <math>f_0 = 0, f_1 = 1, f_2 = 1, f_3 = 2, f_4= 3, f_5 = 5, f_6 = 8</math>, prove que : <math>f_0 + f_2 +f_4 + f_6 +...+ f_{2n} = f_{2n+1} - 1</math> Seja <math>P(n)</math> a afirmação <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2n} = f2_{2n+1} - 1</math> '''Passo base:''' <math>P(0)</math> afirma que <math>f_0 = 0</math> e <math>f_{2*0+1} - 1 = f_1 - 1 = 1 - 1 = 0</math> '''Passo Indutivo:''' <math>P(k) \to P(k+1)</math>, suponha que <math>p(k)</math> é verdadeiro para algum inteiro não negativo <math>k</math> , isto é, <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2k} = f_{2k+1} - 1</math> devemos mostrar que <math>f_0 + f_2 + f_4 + f_6 +...+ f_{2(k+1)} = f_{2(k+1)+1} - 1</math> é verdadeiro, isto é, <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = f_{2k+3} - 1</math>: <math>f_0 + f_2+ f_4 + f_6 + ... + f_{2k+2} = (f_0 + f_2 + f_4 + ... + f_{2k}) + f_{2k+2} = (f_{2k+1} - 1) + f_{2k+2} = f_{2k+1} + f_{2k+2} - 1 = f_{2k+3} - 1</math> Portanto , pelo princípio da indução matemática , <math>f_0 + f_2 + f_4 + f_6 + ... + f_{2n+1} = f_{2n+1} - 1</math> é verdadeiro para todo <math>n \ge 0</math>. ===Implementações em C++(Exemplos 1, 2, 3 e 4)=== Os códigos aqui inseridos podemos notar que foi uitlizado a biblioteca math.h para utilização da função POW, com a qual calculamos a potência de uma determinada base. [[File:Captura de tela de 2016-05-29 20-22-52.png]] [[File:Captura de tela de 2016-05-29 20-23-27.png]] [[File:Captura de tela de 2016-05-29 20-24-41.png]] [[File:Captura de tela de 2016-05-29 20-25-21.png]] [[File:Captura de tela de 2016-05-29 20-25-36.png]] ==Conclusão== A elaboração deste trabalho nos proporcionou um novo conhecimento sobre a liguagem Maple e como podemos aplica-la no entendimento da Indução e Recursão na Matemática em Fundamentos da Matemática para a Computação II. Com isso podemos perceber o quanto é importante o aprendizado contínuo na área da Tecnologia da Informação para nos mantermos atualizados e em crescente aprimoração seja acadêmica ou profissional . ==Referências== Discrete Mathematics and Its Applications. Disponível em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/maple.html> Acesso em: 12 de Maio de 2016. Discrete Mathematics and Its Applications, Disponivel em: <http://www.mhhe.com/math/advmath/rosen/r5/student/ch03/sec03.3/examples.html> Acesso em: 25 de Maio de 2016. 6a510c344cc7a096d0828c897b0357eb37b9bcdc Álgebra Booleana 0 124 784 573 2016-06-03T12:38:22Z Clah 19 wikitext text/x-wiki Muitas situações podem ser, e são, modeladas usando um sistema que tem-se um de dois estados quaisquer. Dependendo do contexto, o par de estados pode ser conhecido por verdadeiro e falso, ou ligado e desligado ou bom e mau, e assim por diante. Exemplos teóricos que vêm à mente ao mesmo tempo são as afirmações precisas que são feitas em ciências matemáticas e físicas, que são considerados como sendo verdadeiro ou falso. Entre as aplicações mais importantes está a modelagem de circuitos digitais em computadores, em que interruptores eletronicos podem também ser ligados ou desligados. Agora, veremos como Maple pode ser usado para manipular sistemas algébricos aritméticos, e para modelar as suas leis, ou regras, simbolicamente. Assim também, podemos modelar a aritmética da álgebra booleana. Na verdade, álgebra booleana é de certo modo mais simples que álgebra numérica, por isso é mais fácil. (Pelo menos, será, uma vez que ela se torne familiar para você). == '''Funções Booleanas''' == No Maple, há um construtor do tipo '''booleano''', isto é, um tipo de variável que pode ser usada para representar valores booleanos. Existem apenas dois valores booleanos e no Maple eles são representados pelos literais '''true''' e '''false'''. Para o Maple, estes são dois valores constantes, assim como o numero '''2''' ou a matriz identidade 3x3 são constantes. Se você atribuir alguma dessas duas constantes para uma variável então o valor da variável será um valor constante. <pre>a := true; b := false;</pre> Dois valores constantes por si só não são muito interessantes. Para fazer coisas úteis, nós precisamos ser capazes de realizar operações significativas sobre elas. Para tal, ''Maple'' oferece dois conjuntos de operadores booleanos para realizar operações em variáveis booleanas e literais. O primeiro conjunto consiste nos operadores '''and''', '''or''' e '''not'''. Em ''Maple'' também é disponibilizado os operadores '''&and''', '''&or''' e '''&not''' para operar sobre literais booleanos e variáveis com simplicidade. Eles estão disponíveis no pacote '''logic''' da linguagem. A diferença entre os dois conjuntos esta na forma em que as expressões formadas usando eles são simplificadas, por exemplo: <pre>true and false; true &and false;</pre> O primeiro operador '''and''' produz seu valor imediato, enquanto que o segundo '''&and''' é mais útil para trabalhar simbolicamente com expressões booleanas, ou seja, para estudar a forma de uma expressão ao invés de seu valor. Um pequeno cuidado é necessário em como usar o operador '''&not'''. É muito importante que as expressões sejam suficientemente definidas com parenteses (Primeiramente, retiramos qualquer valor de '''a''') <pre>a := 'a';</pre> Enquanto <pre>not not a;</pre> é um código ''Maple'' perfeitamente válido, a construção <pre>&not &not a;</pre> conduz, como podemos ver, a um erro de sintaxe. Ao invés disso, a ultima expressão deve ser escrita como <pre>&not (&not a);</pre> provendo nível extra com os parênteses. O operador booleano '''and''' é um operador binário, ele modela a semântica lógica '''and'''. <pre>true and true; true and false; false and true; false and false;</pre> Estes quatro exemplos, exaure todos os possíveis argumentos do operador '''and'''. Nos veremos que o resultado de aplicamos '''and''' para dois valores booleanos é true, precisamente quando o valor de ambos os operandos é '''true'''. Da mesma forma, o exemplo acima mostra que and é um operador comutativo. De fato, o segundo e o terceiro exemplo acima constituem uma prova deste fato. Do mesmo modo, nós podemos mostrar que o operador '''or''' é comutativo. Seu comportamento pode ser completamente determinado pela aplicação dele sobre todos os possíveis pares de seus argumentos. (Faça isto agora!) O operador '''not''', é um pouco diferente dos dois operadores binários '''&and''' e '''or''', no fato que '''not''' é um operador (prefixo) unário. Seu efeito é alternar entre os dois valores booleanos. <pre>not true; not false;</pre> Os resultados não são muito surpreendentes. Os operadores booleanos no Maple tem uma série de outras propriedades das quais você deve estar ciente. Ambos, '''and''' e '''or''' são operadores associativos. Por exemplo, dizer que '''and''' é um operador binário significa que sobre ele é aplicado dois argumentos de uma vez. Se nós desejarmos computar o valor de '''and''' para três valores ditos '''a''', '''b''' e '''c''', então duas expressões distintas serão formadas: '''(a and b) and c''' assim como '''a and (b and c)'''. A propriedade associativa do operador '''and''' assegura que ambas expressões mencionadas tem o mesmo valor. Na verdade, tendo isso sido estabelecido, um argumento indutivo pode ser dado para mostrar que para qualquer sequência do tipo '''a1, a2...an''' de valores booleanos, todas as formas de colocação de parênteses nessa sequência tem o mesmo valor. O resultado disso é que parênteses podem ser descartados. O resumo de essa discussão é que as expressões do Maple, tais como <pre>a and b and c and d;</pre> são inteiramente não ambíguos. Exatamente a mesma coisa é verdadeira para o operador '''or'''. Outra propriedade da qual você deve atentar é que '''not''' não é uma involução. Isto é, <pre>not not a;</pre> Em outras palavras, a segunda aplicação de not desfaz o efeito da primeira. Você pode fazer exatamente a mesma coisa com as tão faladas variantes inertes desses operadores (aquelas com o & prefixado em seus nomes). No entanto, nenhuma simplificação automática ocorrerá. Isto porque operadores booleanos inertes são usados primeiramente para trabalhar ''simbolicamente'' com operações booleanos; eles são operadores sobre os quais as ferramentas do pacote '''logic''' do Maple são baseados. Por exemplo, usando o operador inerte '''&not''', nós veremos que a expressão <pre>&not( &not ( a ) );</pre> Não é simplesmente '''a'''. === '''Um Avaliador Booleano''' === Antes de prosseguir, será conveniente introduzir uma nova função do Maple '''evalb'''. Este é um avaliador geral para expressões booleanas . A expressão booleana é simplesmente uma expressão válida no Maple construída a partir de valores booleanos, variáveis ​​e operadores. No entanto, também é possível produzir valores booleanos a partir de outros tipos de expressões no Maple, como nas expressões aritméticas. Por exemplo, a expressão 2 = 3 e 3.14 = 3.14 são duas expressões aritméticas cujos valores são booleanos, isto é, '''true''' ou '''false'''. A função '''evalb''' permite avaliar expressões como estas como expressões booleanas valoradas no Maple. <pre>evalb(2 = 3); evalb(3.14 = 3.14);</pre> É geralmente a partir de expressões como estas (ou seja, práticas) que usualmente são gerados valores booleanos. Você já viu isso ser usado, muitas vezes, nas cláusulas de teste de condicional ( if ... then ... else ) e delarações de ''looping'' ( for, while ) no Maple. Antes de irmos muito adiante, vale salientar que Maple realmente compreende um terceiro valor booleano, '''‘fail’'''. Este é um pouco diferente do discutido no livro, onde apenas dois valores booleanos são reconhecidos. O valor '''fail''' é uma adição útil em uma linguagem de programação como Maple, uma vez que pode ser utilizado para indicar que um determinado cálculo não foi completamente processado com êxito. Não deve se confundir '''fail''' com '''false'''; '''fail''' é um valor usado para indicar um erro de cálculo, não um valor booleano normal. === '''Representando Funções Booleanas''' === Vamos ver agora como nós podemos representar funções booleanas no Maple. Estas são exatamente como qualquer outra função e pode ser criada usando o comando '''proc'''. Por exemplo, a função booelana escrita (usando a notação do livro) como: ''F(z,y,z) = xy + yz + zx'' pode ser escrita no Maple com o seguinte procedimento <pre>F := proc(x, y, z) RETURN((x and y) or (y and z) or (z and x)); end:</pre> A tradução como você pode ver é bastante simples. Um produto como xy é traduzido diretamente para expressões no Maple como x and y, enquanto a ''sum x+y'' é traduzida como x or y. Se vocẽ imaginar que cada produto xy tem um ponto infixo como ''x.y'', então uma simples regra de substituir cada ponto ( . ) por um operador '''and''' e também trocar cada + pelo operador '''or'''. Os parênteses na hora de definir '''F''' acima não são realmente necessários mas ajudam na leitura do programa. (Porque parênteses bem usados nunca doem). === '''Verificando Identidades Booleanas''' === É relativamente simples usar Maple para verificar identidades booleanas. Para este tipo de trabalho, nós podemos usar os operadores booleanos inertes. Por exemplo, nós podemos checar as leis distributivas como a seguir: <pre>with(logic); # for 'bequal()' left := x &or (y &and z); right := (x &or y) &and (x &or z); bequal(left, right);</pre> Aqui, nós usamos a biblioteca Maple '''bequal''', que testa se duas expressões booleanas são equivalentes (retornando um dos valores booleanos '''true''' ou '''false''' conformemente). Você precisa ter o pacote '''logic''' carregado para usar essa função. Se duas expressões booleanas não são logicamente equivalentes, isto pode ser interessante para determinar algumas atribuições de valor para as variáveis que estão nas duas expressões as quais determinam a identidade inputável a falhar. A procedure '''bequal''' pode ser dada como um terceiro argumento optativo sob o qual irá ser dado uma tal atribuição se o valor falso for o resultado. === '''Dual''' === No Maple, existe uma procedure para encontrar a dupla de uma explressão booleana. Lembre-se que a dupla de uma expressão booleana é obtida trocando cada ocorrência de '''and''' e '''or''' por '''or''' e '''and''' respectivamente. Para usar essa procedure você deve carregar o pacote '''logic'''; <pre>with(logic):</pre> A procedure é chamada '''dual''' (naturalmente) e recebe como argumentos a expressão booleana formada usando as versões inertes dos operadores booleanos. <pre>dual(false); dual(true); dual(x &and y); dual(x &or (&not y &or &not x and &not (&not z)));</pre> A beleza da dualidade é que, uma vez que você provar uma identidade booleana, você pode usar o '''dual''' a vontade! Enquanto é possível usar Maple para provar uma identidade pela força bruta, isto é, checando cada valor possível das variáveis, o pacote '''logic''' oferece uma solução mais elegante. Como um exemplo disto, vamos usar o Maple para provar a identidade [[File:imagem2.png|200px]] e formular nossas expressões usando operadores inertes. <pre>with(logic): # don't forget to define 'bequal'. left := (x &and &not y) &or (y &and &not z) &or (z &and &not x); right := (&not x &and y) &or (&not y &and z) &or (&not z &and x); bequal(left, right);</pre> Agora, nos usamos esta afirmação à vontade. <pre>dual(left); dual(right); bequal(%, %%);</pre> === '''Forma Normal Disjuntiva''' === O Maple provem, em seu pacote '''logic''', uma função para computar a disjunção normal para uma expressão booleana. Esta função é chamada '''canon''' (de canônico). Os exemplos a seguir tipificam a chamada sintaxe <pre>with(logic): canon((a &or b) &and (c &and d), a,b,c,d); canon((a &or b) &and (&not a &or b), a,b); canon((a &or b) &and (&not a &or b), b);</pre> O último exemplo mostra que deve haver pelo menos variáveis suficientes ​​para explicar as que figuram no primeiro argumento. O primeiro argumento para '''canon''' é a expressão a ser transformada, e o segundo argumento é o conjunto de variáveis ​​que aparecem na forma normal disjuntiva . É possível especificar as variáveis ​​que não aparecem na equação original. <pre>canon(a, a,b);</pre> Na verdade, há um terceiro argumento, opcional para '''canon''', que especifica qual forma canônica produzir. Tanto em um dos três valores '''DNF''' (forma normal disjuntiva, o padrão), '''CNF''' (forma normal conjuntiva), ou '''MOD2''', que direciona '''canon''' para converter o seu primeiro argumento para uma expressão aritmética modulo 2 equivalente (na forma canônica). <pre>canon((a &or b) &and (&not a &or &not b), a,b, DNF); canon((a &or b) &and (&not a &or &not b), a,b, CNF); canon((a &or b) &and (&not a &or &not b), a,b, MOD2);</pre> == '''Representação de Funções Booleanas''' == Vimos anteriormente como, dada uma expressão booleana, é muito fácil escrever um procedimento Maple que é representado por essa expressão booleana. É uma questão simples de envolver uma expressão em uma chamada de procedimento. Nesta seção, vamos olhar para o que pode ser considerado, em certo sentido, o problema oposto. Isto é, dada uma função booleana, expressa como uma tabela de valores, como podemos encontrar uma expressão booleana que a representa? Agora, precisamos entender primeiro que, uma vez que a álgebra booleana lida com um domínio de apenas dois valores, certas simplificações são possíveis, o que não estaria presente se estivéssemos lidando com, digamos, funções reais. Agora podemos ver por que álgebra booleana é, de certa forma, mais simples do que algumas outras áreas da álgebra. A fim de especificar uma função booleana-valorizada f (qualquer que seja seu domínio), é necessário apenas especificar que valores do domínio de f são mapeados para 1. O resto do domínio de f deve ser mapeado para 0! (De mesmo modo, pode-se especificar que valores de f são mapeados para 0, e o restante seria necessariamente mapeados para 1.) Isso funciona porque as pré-imagens dos pontos no contradomínio de qualquer função de partição de domínio da função. Para as funções booleano-valorizado, existe exatamente dois conjuntos na partição, um para cada um dos valores booleanos em seu contradomínio. (Um dos dois conjuntos pode estar vazio.) Esta ideia é a chave para o método descrito no texto para determinar a expressão booleana representando uma dada função booleana, e é o princípio sobre o qual devemos basear nosso procedimento Maple para computar tais expressões. Vamos escrever um programa Maple que, quando alimentada a pré-imagem sob uma função booleana do valor '''true''', computará uma expressão booleana que representa aquela função. Depois, devemos ver uma técnica para achar uma boa expressão que represente uma determinada função. O procedimento que devemos escrever aqui computará a chamada representação da '''soma de produtos'''. O primeiro passo é projetar a(s) entrada(s) do procedimento. Como foi discutido acima, apenas é necessário especificar a pré-imagem de (digamos) '''true''' (isto é, de 1) de acordo com nossa função. Isso significa que precisamos entrar com os valores como n-tuplas que, baseado na aplicação de nossa função, produz o valor '''true'''. Vamos considerar uma função bem simples ''f (x, y, z)'', de três variáveis, com a seguinte tabela de verdade. <pre>x y z 0 0 0 1 0 0 1 0 0 1 0 1 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 1 1 1 1 1</pre> Temos que especificar que a tripla ''(a,b,c)'', no domínio de ''f'', são mapeadas para 1. No caso, esta é a lista (0 0 0), (0 1 0), (0 1 1), (1 1 0), (1 1 1) de triplas. Então, este é o tipo de entrada que devemos fornecer ao noso procedimento. Uma vez que isso é feito, resulta que os pontos restantes (0 0 1), (1 0 0), (1, 0, 1) no domínio de ''f'' são mapeados para 0 - não é necessário especificar isso diretamente. Agora, uma vez que sabemos quais n-tuplas no domínio de nossa função são mapeados por ela para 1, precisamos encontrar um '''MinTerm''' (termo mínimo) correspondente para cada uma dessas n-tuplas. Vamos escrever isso como uma subrotina para nosso procedimento principal <pre>getMinTerm := proc(ll) local e, # the minterm, to be returned t, # temporary variable i; # loop index</pre> checar argumento, <pre> if not type(ll, list(boolean)) then ERROR(`expecting a list of boolean values`); fi;</pre> fazer a construção, <pre> for i from 1 to nops(ll) do if ll[i] = true then t := `x`.i; else t := `not x`.i; fi; if i = 1 then e := t; else e := cat(e, ` and `, t); fi; od;</pre> e adicionar parênteses para melhorar a legibilidade. <pre>RETURN(cat(`(`, e, `)`)); end:</pre> Este procedimento implementa o algorítmo do texto para computação de um minitermo para representar uma pré-imagem de 1 (representada no Maple como '''true'''). Para encontrar o minitermo correspondente para uma tripla (1,0,1) que, no Maple é escrita como ''true, false, true'', computamos <pre>getMinTerm([true, false, true]);</pre> Ao ter determinado '''MinTerm''' para cada uma das pré-imagens de 1, tudo que resta a fazer é determinar a soma desses '''MinTerm'''. Isto será realizado pelo procedimento principal de nosso programa <pre>SumOfProductsExpansion := proc() local e, # expression to return i; # loop variable</pre> se não há argumentos, a função é identicamente ‘false’ <pre> if nargs = 0 then RETURN(`false`); fi; for i from 1 to nargs do if not type(args[i], list(boolean)) then ERROR(`arguments must be lists of booleans`); fi; if i = 1 then e := getMinTerm(args[i]); else e := cat(e, ` or `, getMinTerm(args[i])); fi; od; RETURN(e); end:</pre> Podemos usar este programa para encontrar uma expressão booleana que representa a nossa função de exemplo a seguir. <pre>SumOfProductsExpansion( [false, false, false], [false, true, false], [false, true, true], [true, true, false], [true, true, true] );</pre> == '''Minimização de Expressões Booleanas e Circuitos''' == Nossa função para encontrar a soma da expansão de produtos de uma função booleana pode levar a circuitos ineficientes, porque o número de portas requeridas para implementá-los diretamente pode ser muito bem superior ao número que é realmente necessário. No entanto, o número de funções booleanas distintas de n variáveis (em que n é um inteiro positivo) é finito - de fato, é igual a <math>2^{2^{n}}</math>, como mostrado no texto - é fácil visualizar que o número de expressões booleanas distintas sobre n variáveis é infinito. Algumas formas do Princípio da Casas dos Pombos nos obriga a concluir que algumas funções booleanas têm muitas - na verdade, infinitamente muitas - representações distintas através de expressões booleanas. Da perspectiva de projeto de circuito, portanto, o que é necessário é um método para ''minimizar'' um circuito, no sentido em que se deseja, dado um circuito, encontrar um circuito equivalente que usa o menor número de portas quanto possível. A fim de fazer o Maple fazer isso para nós, devemos traduzir o problema da linguagem pictórica de diagramas de circuitos para uma descrição algébrica envolvendo expressões booleanas, reconhecendo que um diagrama de circuito é uma representação pictórica de uma expressão booleana equivalente, onde em uma porta lógica simples representa-se um dos operadores padrões booleanos: '''and''', '''or''' e '''not'''. Para tornar isso um pouco mais concreto, vamos ver um exemplo simples. A expressão booleana ''xy + xȳ'' que, representada na sintaxe de Maple, parece <pre>e := (x &and y) &or (x &and &not y);</pre> pode ser minimizada atarvés da utilização de Maple da seguinte forma <pre>with(logic): distrib(x &and (y &or &not y));</pre> que mostra que as expressões ''x ( y + ȳ ) and xy + xȳ'' representam a mesma função booleana. Porém ''y + ȳ = 1'', para qualquer y <pre>bequal(y &or &not y, true);</pre> de modo que ''x ( y + ȳ )'' simplifica ainda mais para x. O truque consiste em detectar, para uma determinada expressão booleana, as oportunidades para eliminar variáveis, ou reduzir o número de '''MinTerm''', usando as propriedades algébricas de operadores booleanos. Para o exemplo simples acima, este foi muito fácil, e Maple apenas nos permitiu provar que nossas suposições estavam corretas. Mas expressões que são apenas um pouco mais complicadas podem exigir muito mais para detectar tais simplificações. O que precisamos é algo que vai nos permitir trabalhar na direção oposta à que foi tomada acima, ou seja, dada a expressão original, podemos realmente encontrar uma expressão mais simples a que é equivalente? Além disso, podemos encontrar uma que é mínima? Felizmente, o pacote de '''logic''' de Maple proporciona um minimizador de circuito que cuida de tudo isso para nós. Ele é chamado '''bsimp'''. Para usar esse método, você deve carregar o pacote '''logic''' primeiro em sua sessão Maple, ou chamá-lo pelo nome completo '''logic[bsimp]'''. Claro, o Maple não fala diretamente em termos de portas e circuitos. Para solicitar ao Maple para minimizar um circuito, você deve falar a linguagem algébrica de Maple, especificando a expressão booleana equivalente. Por exemplo, para simplificar o exemplo anterior <pre>e := (x &and y) &or (x &and &not y);</pre> você pode digitar <pre>with(logic): # load 'bsimp' bsimp(e);</pre> Você pode aplicar '''bsimp''' a qualquer expressão booleana formada usando os operadores booleanos inertes do pacote '''logic'''. Vamos ver como Maple lida com alguns exemplos mais complicados. <pre>with(logic): e := (w &and x &and y &and (&not z)) &or (w &and (&not x) &and y &and z) &or (w &and (&not x) &and y &and (&not z)) &or ((&not w) &and x (&not y) &and z) &or ((&not w) &and (&not x) &and y &and z) &or ((&not w) &and (&not x) &and (&not y) &and z);</pre> O procedimento '''bsimp''' é muito complexo, e usa um algoritmo de Quine-McCluskey baseado na representação de expressões booleanas como conjuntos. Estas estruturas de dados, embora muito natural para Maple, não correspondem muito bem com a descrição dada no texto. == '''Condições Indiferentes''' == É possível usar o nosso procedimento '''SumOfProductsExpansion''' para lidar com as chamadas condições indiferentes. Aqui, vamos desenvolver um procedimento Maple que nos permite calcular uma soma de produtos em expansão (forma normal disjuntiva), de comprimento mínimo, para uma função booleana especificada juntamente com condições indiferentes. Informalmente, um conjunto de condições indiferentes para uma função booleana f é um conjunto de pontos no domínio de f cujas imagens em f nós não estamos interessados. Em outras palavras, somos indiferentes a respeito para onde f envia esses pontos. Há provavelmente alguns pontos do domínio de f, no entanto cujas imagens sob f são importantes para nós. Estes pontos em que f é bem definida forma um subconjunto A do domínio de f, e a restrição de f para A é uma função bem definida (no sentido de que não existe ambiguidade em relação ao valor de f em qualquer destes pontos). Nota-se que, se f é para ser uma função de n variáveis, então o domínio D é simplesmente o conjunto {0, 1}^n de todos n-tuplas de 0's e 1's (ou, em sintaxe Maple, o conjunto {true, false}. Se pensarmos em f como uma função totalmente definida neste subconjunto A de D, então, o que nos interessa é a família de todas as extensões de f a D. Que é o conjunto de todas as funções booleana valorada g de D, cuja restrição a A é igual a f. Agora, cada uma destas funções g é completamente definida em D, de modo que a técnica usada anteriormente para calcular a expansão da soma dos produtos pode ser aplicada a qualquer um deles. Assim, para encontrar uma expansão da soma de produtos ideal (que, aqui, significa menor) de f, podemos calcular a unica expansão de soma de produtos para cada extensão g de f para D, e procurar entre eles por um de tamanho mínimo. Devemos parar para considerar o tamanho desse problema. O subconjunto A de D em que f é bem-definida, e o subconjunto DC de pontos indiferentes (don’t care points), em que f não é especificada na partição o domínio D, isto é, D deve ser escrito como uma união disjunta [[File:imagem.png|100px]]. Se temos d como um ponto indiferente (isto é, se |D| = d), então temos <math>2^{d}</math> extensões g de f para D. Cada extensão corresponde a uma escolha de um subconjunto de DC para incluir entre ele e as pré-imagens de 1. Então, o problema cresce muito rapidamente com o número de pontos indiferentes, ou condições. A ótica que adotamos aqui faz com que seja muito fácil ver um algorítmo que calcula uma expansão da soma de produtos, de tamanho mínimo, para uma função determinada com condições indiferentes. Podemos simplesmente fazer uma pesquisa exaustiva sobre o conjunto de todas as extensões bem definidas. Para fazer isso, rescreveremos o procedimento Maple '''dcmin''' (Don’t Care Minimizer - Minimizador Indiferente) para construir todas as <math>2^{d}</math> funções, chame nosso procedimento '''SumOfProductsExpansion''' sobre cada, e depois olhe para um que tenha o comprimento mínimo. Aqui está o código Maple para fazer isso. <pre>dcmin := proc(pt::set,list, dc::set,list) local e, # expression to return te, # temporary expression i, # index s, # the size of the smallest expression so far PT, # pt as a set DC, # dc as a set PDC, # power set of DC T, # temporary set (loop variable) S; # temporary domain for well-defined functions PT := op(pt); DC := op(dc); PDC := combinat[powerset](DC); s := infinity; for T in PDC do S := T union PT; te := SumOfProductsExpansion(op(S)); if evalb(length(te) < s) then e := te; s := length(e); fi; od; if s = infinity then ERROR(`can't happen`); else RETURN(e); fi; end:</pre> Isto é simplesmente uma solução de força bruta para o problema. Rodamos em loop sobre todos os conjuntos possíveis de valores no conjunto de indiferentes '''DC''' que, com efeito, nos permite especificar uma função bem definida exclusiva para a entrada de '''SumOfProductsExpansion'''. Examinemos o comprimento da expressão '''te''' retornada para cada entrada, e, se essa se revelar como menor do que qualquer outra expressão vista até o momento, gravamos como o novo valor de '''e'''. Quando todas as possibilidades forem esgotadas, a variável '''e''' irá conter uma expressão de menor comprimento que representa a função de entrada. Perceba que existe, de fato, várias expressões que o comprimento é igual a este valor mínimo. Nosso procedimentos retorna a primeira expressão que é encontrada com esse tamanho. Temos feito este procedimento um pouco mais amigável, projetando-o para que ele aceite ou um par de listas ou um par de conjuntos como entrada. Note que, como adotado aqui, o comprimento de uma expressão é simplesmente uma medida da sua complexidade. Você pode, por exemplo, desejar contar o número de operadores booleanos na expressão e minimizar esse número. Você poderia mudar este procedimento simplesmente substituindo a função '''length''' por alguma outra medida de complexidade de expressões. Agora que temos um procedimento para minimização de funções Booleanas, vamos usá-la com alguns exemplos. Considere uma função ''f (x, y, z)'' booleana com a seguinte tabela verdade, em que um d na coluna mais a direita indica uma condição indiferente. <pre>x y z false false false true false false true false false true false d true false false d false true true true true false true false true true false false true true true true</pre> A entrada é o conjunto <pre>\{false, false, false, false, true, true, true, true, true\}</pre> De pontos mapeados para '''true''' (a pré-imgem '''pt''' de '''true'''), e o conjunto <pre>\{false, true, false, true, false, false\}</pre> de pontos que nós não nos importamos, ou seja, conjunto de indiferentes. Podemos calcular a expansão da soma de produtos para esta função como pode ver a seguir. <pre>dcmin( [false, false, false], [false, true, true], [true, true, true], [false, true, false], [true, false, false]);</pre> Como mencionado acima, poderíamos muito bem ter representado a entrada como duas listas: <pre>dcmin( [[false, false, false], [false, true, true], [true, true, true]], [[false, true, false], [true, false, false]]);</pre> (O primeiro é mais legível, enquanto o último é mais consistente com a entrada para '''SumOfProductsExpansion'''). == '''Cálculos e Explorações''' == Nesta seção, vamos olhar para Problemas 2 e 6 da seção de computações e explorações do texto, e ver como podemos usar Maple para resolver alguns deles. Construa a tabela da função booleana de grau 3. '''Solução''' Primeiro, nós devemos notar que existem <math>2^{2^{3}}</math> funções booleana de grau 3, então a saída da nossa computação será longa. Podemos usar a função '''SumOfProductExpansion''' que desenvolvemos anteriormente para nos ajudar com esse cálculo. Lembre que a expansão da soma dos produtos, ou forma normal disjuntiva, da função booleana dará uma correspondência bijetiva entre funções booleanas e certas expressões booleanas. Embora haja um número infinito de expressões booleanas, há precisamente <math>2^{2^{n}}</math> formas normais disjuntivas com n variáveis, e elas estão em bijetiva correspondência com o conjunto de todas as funções boolenas de n variáveis, portanto essa é a conveniente representação de uso. Para gerar a lista inteira nós precisamos especificar todas as possibilidades distintas de listas de argumentos de '''SumOfProductsExpansion'''. Isso significa que nós devemos gerar o subconjunto do domínio de todas as funções boolenas de três variáveis. Agora, a função booleana f de três variáveis tem domínio igual a <pre>Dom3 := [false,false,false], [false,false,true], [false,true,false], [true,false,false], [true,false,true], [true,true,false], [false,true,true], [true,true,true];</pre> Podemos gerar esse subconjunto usando o procedimento '''powerset''' no pacote '''combinat'''. Portanto, devemos primeiro carregar o pacote '''combinat'''. <pre>with(combinat):</pre> Note o formulário de saída. Nós obtemos o conjunto dos conjuntos, enquanto nosso procedimento '''SumOfProductsExpansion''' requer uma expressão sequência de listas booleanas. Isso significa que precisamos usar a função '''op''' do Maple também. Agora podemos gerar a lista de funções booleanas em três variáveis usando um loop de '''for''' simples: Você pode querer escrever a saída para um arquivo para ter melhores condições de examiná-lo. Você pode fazer isso usando a função '''printf''' no lugar de '''print'''. Alternativamente, você pode usar as funções '''writeto''' ou '''appendto''' para redirecionar a saída Maple para um arquivo. Assim que estiver pronto, você pode restaurar a saída do terminal, emitindo a chamada <pre>writeto(terminal);</pre> Use a facilidade help do Maple para saber mais sobre essas funções, se necessário. Randomicamente, gere dez expressões de grau 4 e determine a média do número de passos para minimizá-los. '''Solução''' Há realmente duas partes para este problema: Primeiro, precisamos encontrar uma maneira de gerar expressões booleanas aleatórias. Em segundo lugar, temos de encontrar algum método de examinar o processo de minimização para que possamos contar os passos. Maple oferece uma solução fácil para a primeira parte do problema. No pacote '''logic''', existe um procedimento chamado '''randbool''' que gera uma expressão booleana aleatória. Uma vez que é parte do pacote de lógica, ele poderá ser definido antes de ser utilizado. <pre>with(logic):</pre> Para usar '''randbool''', você precisa especificar um alfabeto sobre o qual construir expressões booleanas. Por exemplo, para gerar uma expressão booleana aleatória no símbolos '''a''' e '''b''', você pode digitar <pre>randbool(a,b);</pre> ou <pre>randbool([a,b]);</pre> Ou seja, o alfabeto pode ser especificado como um conjunto ou como uma lista. Note que os resultados das duas chamadas acima são diferentes - as expressões são geradas randomicamente. (Isso é verdade mesmo após o '''restart'''. Você pode tentar isso também, mas não se esqueça de carregar o pacote '''logic''' novamente). Agora, para gerar dez expressões booleanas aleatórias, podemos simplesmente usar um “loop” '''for''': <pre>for i from 1 to 10 do randbool(a,b); od;</pre> Note também que as expressões são gerados na forma normal disjuntiva, isto é, uma soma de produtos de expansão. Também é possível especificar um segundo argumento '''randbool''' de que afeta a forma das expressões geradas. O segundo argumento pode ser qualquer um entre os valores '''DNF''', '''CNF''' ou '''MOD2''', assim como o procedimento '''canon''' que nos conhecemos antes. <pre>randbool(x,y,z, CNF); randbool(u,v, MOD2);</pre> Tendo resolvido a primeira parte do problema, precisamos encontrar uma maneira de contar o número de passos dados durante o processo de minimização. Existem três abordagens que podemos tomar para esta parte do problema. A primeira é a de medir o tempo necessário para executar um procedimento. Você já viu isso antes em capítulos anteriores. O segundo é a facilidade de rastreamento para monitorar o número de passos dados para executar uma minimização. Você pode rastrear um procedimento de bordo emitindo a chamada <pre>readlib(trace);</pre> Agora, se gerarmos um expressão aleatória booleana com quatro variáveis <pre>e := randbool(a,b,c,d);</pre> poderemos observar o número de passos necessários para simplificá-lo simplesmente chamando '''bsimp''' depois de rastreá-lo <pre>bsimp(e);</pre> O traçado de '''bsimp''' irá imprimir uma série de declarações do formulário B: = < algo >, que você pode contar. Nós não os temos mostrado aqui. O procedimento '''trace''' na verdade faz com que a execução de '''bsimp''' para imprimir mais informações do que precisamos. Algumas dessas informações podem ser ignorados para este problema. Cada instrução executada é impressa, como são os argumentos e valores de retorno de qualquer sub-rotinas chamadas. Nós queremos apenas contar o número de instruções executadas, de modo que a outra informação pode ser simplesmente ignorada. Para anular o efeito do procedimento '''trace''', você pode desfazer a função '''trace''' simplesmente com o procedimento '''untrace''': <pre>untrace(bsimp); # 'bsimp' deixará de ser rastreada</pre> Finalmente, para obter o máximo de informações, podemos definir a variável '''printlevel'''. A variável '''printlevel''' é um pouco confusa, ou seja, não se sabe se ela é global ou local. Como qualquer variável global, é visível no nível superior do Maple. No entanto, o seu valor é alterado cada vez que entra em uma estrutura de controle, como um loop, ou no corpo do procedimento e, em seguida, reseta para seu valor original depois de sair da estrutura. Normalmente, '''printlevel''' é definido como 1. <pre>Printlevel;</pre> Mas é possível atribuir um valor a '''printlevel''' que afetará o quanto é impresso dentro das estruturas de controle. <pre>for i from 1 to 5 do sqrt(i); od;</pre> Experimente o ciclo anterior depois de definir o valor de '''printlevel''' para algo como 16. Cada nível aninhado de invocação do procedimento '''printlevel''' é decrementado por 5. Assim, a criação '''printlevel''' a 6 no nível superior é muito parecido com o rastreamento (usando '''trace''') de todos os procedimentos do Maple. Para fazer Maple mostrar o que se está fazendo a ainda maiores níveis de chamadas de função, simplesmente defina '''printlevel''' para um valor alto. <pre>for i from 1 to 5 do sqrt(sin(abs(i))); od;</pre> Por razões tipográficas, a volumosa produção foi suprimida neste manual impresso, mas você pode ver os resultados espetaculares na tela do computador. Tente fazer esse último exemplo com '''printlevel''' definindo um valor muito grande como 100000. Agora, para realmente ver o que está acontecendo quando você invocar '''bsimp''' para minimizar a expressão booleana, você pode definir '''printlevel''' a um enorme valor, e observar os resultados. <pre>with(logic): # esteja certo que 'bsimp' está definida e := x &and y &or &not z; bsimp(e);</pre> Para interpretar os resultados de saída, é aconselhável ser capaz de olhar para o código-fonte do procedimento '''bsimp''' e as sub-rotinas da biblioteca que ele chama. Você pode fazer isso emitindo o seguinte <pre>interface(verboseproc = 2); eval(bsimp); # assuma 'bsimp' carregada</pre> Isso mostra o código fonte real para a função '''bsimp''', embora comentários estão faltando. (Não pode haver comentários porque Maple desmonta o código objeto na biblioteca do Maple, a partir do qual a compilação foi despojado de todos os comentários, para produzir o código de Maple que você vê aqui). Para entender a saída, você deve perceber que o Maple não fornece acesso a operações de nível de bits, de modo que o algoritmo usado em '''bsimp''' é um pouco diferente do que a descrita em seu livro. Em vez de cadeias de bits, Maple usa conjuntos para representar expressões booleanas. Com essas ferramentas em mãos, agora você pode escrever um procedimento para gerar, aleatoriamente, dez expressões booleanas em quatro variáveis​​, e contar o número de passos necessários para minimizar cada um, finalmente a tomar uma média. == '''Exercícios e Projetos''' == 01) Use o Maple para verificar a Lei DeMorgan e as leis de comutatividade e associatividade: .: Leis de DeMorgan :. Equivalent(`&not`(`&or`(A, B)), `&and`(`&not`(A), `&not`(B))); true Equivalent(`&not`(`&and`(A, B)), `&or`(`&not`(A), `&not`(B))); true .: Comutatividade :. Equivalent(`&and`(A, B), `&and`(B, A)); true Equivalent(`&or`(A, B), `&or`(B, A)); true .: Associatividade :. exp1 := `&and`(A, `&and`(B, C)); exp2 := `&and`(`&and`(A, B), C); Equivalent(exp1, exp2); true exp3 := `&or`(A, `&or`(B, C)); exp4 := `&or`(`&or`(A, B), C); Equivalent(exp3, exp4); true 02) Use Maple para construir as tabelas verdades para cada um dos seguintes pares de expressões booleanas. a) a -> b and b -> a with(Logic): TruthTable(((a &implies b)&and(b &implies a)), [a,b], output = Matrix); table([ (true, true) = true, (false, true) = false, (false, false) = true, (true, false) = false ]) b) a -> ¬b and b -> ¬a with(Logic): TruthTable(`&and`(`&implies`(a, `&not`(b)), `&implies`(b, `&not`(a))), [a, b], output = Matrix); table([ (true, true) = false, (false, true) = true, (false, false) = true, (true, false) = true ]) c) a + (b(¬c)) and (a + b +d)(a + c + d) with(Logic): TruthTable(`&and`(`&or`(a, `&and`(b, `&not`(c))), (`&or`(`&or`(a, b), c))*(a+c+d)), [a, b, c, d], output = Matrix); table([ (false, false, true, true) = false, (true, false, false, true) = false, (false, false, false, false) = false, (true, false, true, true) = false, (false, false, false, true) = false, (true, true, false, true) = false, (false, false, true, false) = false, (true, true, false, false) = false, (true, false, false, false) = false, (false, true, true, false) = false, (true, true, true, true) = false, (false, true, true, true) = false, (true, true, true, false) = false, (false, true, false, false) = false, (true, false, true, false) = false, (false, true, false, true) = false ]) == Referências == [http://www.mhhe.com/math/advmath/rosen/r5/student/ch10/maple.html Maple: Chapter 10. Boolean Algebra, Kenneth H. Rosen] 8738bb8f063c01f0286346732d48727ce960d656 File:Q1.jpg 6 191 789 2016-06-18T01:40:22Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e File:Q2.jpg 6 192 791 2016-06-18T02:36:58Z Marcielmanoel15 30 MsUpload wikitext text/x-wiki MsUpload 519c08da88276b2f47bc6fb30637d415fd0d804e Fundamentos Matemáticos da Computação 3 0 193 794 2020-08-18T21:26:33Z Jmarcos 3 link to FMC3c wikitext text/x-wiki [[Introdução Computacional à Lógica Matemática]] 8efcf7c9cb517c46537d3dedfb763e3232383e55 File:Lolita logo.png 6 194 795 2020-08-18T22:17:07Z Patrickts 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 796 795 2020-08-18T22:39:16Z Patrickts 2 Patrickts uploaded a new version of [[File:Lolita logo.png]] wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Lolita.gif 6 195 797 2020-08-18T22:46:19Z Patrickts 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Wiki.png 6 196 798 2020-08-18T23:10:08Z Patrickts 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 File:Faltre.jpeg 6 197 799 2020-08-18T23:41:50Z Patrickts 2 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Criação de Curso 0 7 801 69 2020-08-20T03:25:02Z Admin 1 wikitext text/x-wiki Criar um curso é bem simples: basta que se encontre o menu "Configurações" - ao lado esquerdo, abaixo do menu "Navegação". Então, na aba "Administração do site", clique no item "Cursos": aparecerão opções de configuração e de adição/modificação de cursos. Clique em "Acrescentar/modificar cursos". [[file:Criacao-de-curso_01.jpg|1040px]] Após o segundo clique, abrirá uma página de criação/edição de curso. Nesta página, os cursos são organizados por tipo, mostrados na tabela como "categorias de cursos". Mas como o objetivo é criar um curso, e não modificar um já existente, basta clicar no botão correspondente, que está fora da tabela. [[file:Criacao-de-curso_02.jpg|1040px]] Após clicar no botão para criar um curso, abrirá uma outra página, esta sendo para configuração do curso que será criado. Basicamente, só é preciso preencher os campos obrigatórios para criar, de fato, um curso. Esses campos obrigatórios são: o nome do curso (não existe um curso sem uma identificação), e um "nome breve do curso", que é nada mais do que uma abreviação, ou um código,para o nome do curso - como LIBRAS é uma abreviação para Língua Brasileira de Sinais. Apesar de só serem necessários os campos obrigatórios, é importante que se configure ao menos o formato do curso, que indica como o curso será organizado. No formato de tópicos, por exemplo, o curso será mostrado em, obviamente, tópicos, o que ajuda a ter um melhor controle de como o curso será organizado. Outro ponto importante é a configuração de grupos. A configuração controla a visibilidade dos grupos em relação aos outros grupos. Na "modalidade grupo", define-se como os grupos ficam visíveis uns aos outros: nenhum grupo - todos se vêem, grupos separados - o grupo vê somente as atividades de seu grupo, grupos visíveis - os membros de um grupo podem ver os outros grupos. No campo "forçar modalidade grupo", se a opção for sim, as atividades em grupos são forçadas a ter a mesma configuração de grupos do curso, e em caso contrário, as atividades podem ser configuradas de uma forma diferente do curso. Na configuração de disponibilidade, escolhemos se o curso aparece ou não na página de cursos. [[file:Criacao-de-curso_03.png|1040px]] Ao fim da configuração, basta clicar em "salvar mudanças", e então o curso será criado! :-) [[file:Criacao-de-curso_04.png|1040px]] f9f0e895cdf3415a6f2fde89d56553e06657c930 File:Falsetrue.jpeg 6 198 802 2020-08-20T03:26:59Z Admin 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Introdução Computacional à Lógica Matemática 0 199 809 2020-08-21T03:11:04Z Jmarcos 3 adding links wikitext text/x-wiki [[Sintaxe da Lógica de Primeira Ordem]] 9c3e371fd17150eced2e77a9b281447e003d48ab Editing tricks 0 201 819 2020-08-21T04:19:39Z Jmarcos 3 some code to copy-paste wikitext text/x-wiki {{#ev:youtube|r7gLlIv4ito}} To display the same video as a right aligned large thumbnail with a description: {{#ev:youtube|r7gLlIv4ito|1000|right|Disney|frame}} For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments. {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} <youtube width="200" height="120">r7gLlIv4ito</youtube> 1f0333c6f74b791384887cd24bade70ea3166fcb 821 819 2020-08-21T05:31:12Z Admin 1 wikitext text/x-wiki {{#ev:youtube|r7gLlIv4ito}} To display the same video as a right aligned large thumbnail with a description: {{#ev:youtube|r7gLlIv4ito|1000|right|Disney|frame}} For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments. [[File:Faltre.jpeg|frame|20px]] {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} <youtube width="200" height="120">r7gLlIv4ito</youtube> 3266ed628ca8a79e5c1f7641892863d0983d6a56 824 821 2020-08-21T17:53:23Z Admin 1 wikitext text/x-wiki {{#ev:youtube|r7gLlIv4ito}} To display the same video as a right aligned large thumbnail with a description: {{#ev:youtube|r7gLlIv4ito|1000|right|Disney|frame}} For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments. (α → β) → α [[File:Faltre.jpeg|frame|20px]] {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} <youtube width="200" height="120">r7gLlIv4ito</youtube> 1b33f479e61bc4dbc08acef16aac4372165a528e 825 824 2020-08-21T17:55:40Z Admin 1 wikitext text/x-wiki {{#ev:youtube|r7gLlIv4ito}} To display the same video as a right aligned large thumbnail with a description: {{#ev:youtube|r7gLlIv4ito|100|right|Disney|frame}} For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments. (α → β) → α <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> [[File:Faltre.jpeg|frame|20px]] {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} <youtube width="200" height="120">r7gLlIv4ito</youtube> 8391a8c193293df9d7e84ce188cb1d681d7ca449 826 825 2020-08-21T17:57:30Z Admin 1 wikitext text/x-wiki {{#ev:youtube|r7gLlIv4ito}} To display the same video as a right aligned large thumbnail with a description: {{#ev:youtube|r7gLlIv4ito|200|right|Disney|frame}} For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. Please note that not all video services support extra URL arguments or may have different keys for their URL arguments. [[File:Faltre.jpeg|frame|20px]] {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} <youtube width="200" height="120">r7gLlIv4ito</youtube> === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> c4a5d36149ca2451fbdcfa94c6ba9340c428f355 827 826 2020-08-21T18:01:59Z Admin 1 wikitext text/x-wiki * Display a YouTube video: {{#ev:youtube|r7gLlIv4ito}} * To display the same video as a right aligned large thumbnail with a description: {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} <youtube width="200" height="120">r7gLlIv4ito</youtube> * To display a image, upload the file, and insert the tag: [[File:Faltre.jpeg|frame|20px]] * For more setting check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 0fbf5bf111d5a1f679b394cbf57ce52e1962aacb 828 827 2020-08-21T18:08:54Z Admin 1 wikitext text/x-wiki * Display a YouTube video: <code>{{#ev:youtube|r7gLlIv4ito}}</code> {{#ev:youtube|r7gLlIv4ito}} * To display the same video as a right aligned large thumbnail with a description: <code>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</code> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. <code>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</code> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * Another standart tag is: <code><youtube width="200" height="120">r7gLlIv4ito</youtube></code> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or youtubeplaylist - Playlists, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <code>[[File:Faltre.jpeg|frame|20px]]</code> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <code><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></code> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 18f321c9f4358b73fada4e491cc8ff26d793c334 829 828 2020-08-21T18:09:29Z Admin 1 wikitext text/x-wiki * Display a YouTube video: <code><pre>{{#ev:youtube|r7gLlIv4ito}}</pre></code> {{#ev:youtube|r7gLlIv4ito}} * To display the same video as a right aligned large thumbnail with a description: <code>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</code> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. <code>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</code> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * Another standart tag is: <code><youtube width="200" height="120">r7gLlIv4ito</youtube></code> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or youtubeplaylist - Playlists, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <code>[[File:Faltre.jpeg|frame|20px]]</code> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <code><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></code> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> d936bd25511831df7f65d698b910cefc1477d36d 830 829 2020-08-21T18:11:59Z Admin 1 wikitext text/x-wiki * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * To display the same video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * Another standart tag is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or youtubeplaylist - Playlists, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <code>[[File:Faltre.jpeg|frame|20px]]</code> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 698816348475a4d5b0e94c7b67199466149fab36 831 830 2020-08-21T18:12:24Z Admin 1 /* Images Editing tricks */ wikitext text/x-wiki * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * To display the same video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * For YouTube to have the video start at a specific time code utilize the urlargs(URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the urlargs. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * Another standart tag is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or youtubeplaylist - Playlists, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 9437cb5092effad74850cbe9336b71a67560c0b1 832 831 2020-08-21T18:15:39Z Admin 1 wikitext text/x-wiki * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 4d302bde23cc4d359de60596c1d264dbe23b16d2 File:Negate-conditional-equivalent-with-truth-table.png 6 202 833 2020-08-21T18:19:13Z Admin 1 wikitext text/x-wiki Negate conditional equivalent with truth-table 1b4f52c7218c8121fc754a0d6da52a58daead0b7 Editing tricks 0 201 834 832 2020-08-21T18:36:56Z Admin 1 wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 32f977e5f3c247ca86a9ce29fede483f654b86a9 839 834 2020-08-21T20:19:20Z Admin 1 wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> c265f9e1d2857e32bf08cb33db5e85a98247ea86 840 839 2020-08-21T21:35:39Z Admin 1 wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] <pre>[[File:Fuzzy logic temperature te.svg|thumb]]</pre> [[File:Fuzzy logic temperature te.svg|thumb]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 793225aa8fe257baab5dd7db49867b8d7cb3e2d4 841 840 2020-08-21T21:36:24Z Admin 1 /* Images Editing tricks */ wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] <pre>[[File:Fuzzy logic temperature te.svg|thumb]]</pre> [[File:Fuzzy logic temperature te.svg|Fuzzy logic temperature te.svg]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> ac201fca27defb66fee21528e22fe2769ca2c9aa 842 841 2020-08-21T21:42:31Z Admin 1 wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] <pre>[[File:Fuzzy logic temperature te.svg|thumb]]</pre> [[File:Fuzzy logic temperature te.svg|frame|Fuzzy logic temperature te.svg]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> d501510ac56332bd1a409fa0586d1eceddcd6790 843 842 2020-08-21T21:42:54Z Admin 1 wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] <pre>[[File:Fuzzy logic temperature te.svg|thumb]]</pre> [[File:Fuzzy logic temperature te.svg|Fuzzy logic temperature te.svg]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 22658be5543974730f7dc3b9ba8c6e3dbc056d87 844 843 2020-08-21T21:43:39Z Admin 1 wikitext text/x-wiki === Formatting Editing tricks === * Formatting: https://www.mediawiki.org/wiki/Help:Formatting === Video Editing tricks === * Display a YouTube video: <pre>{{#ev:youtube|r7gLlIv4ito}}</pre> {{#ev:youtube|r7gLlIv4ito}} * For YouTube to have the video start at a specific time code utilize the url args (URL arguments) parameter. Take the rest of the URL arguments from the custom URL and place them into the url args. <pre>{{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}}</pre> {{#ev:youtube|https://www.youtube.com/watch?v=r7gLlIv4ito|||||start=76}} * To display the video as a right aligned large thumbnail with a description: <pre>{{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}}</pre> {{#ev:youtube|r7gLlIv4ito|250|right|Disney|frame}} * Another standard tag for YouTube is: <pre><youtube width="200" height="120">r7gLlIv4ito</youtube></pre> <youtube width="200" height="120">r7gLlIv4ito</youtube> * For more setting on Video tags for Vimeo, soundcloud, TED Talks, or YouTube Playlist, check: https://gitlab.com/hydrawiki/extensions/EmbedVideo === Images Editing tricks === * To display a image, upload the file, and insert the tag: <pre>[[File:Faltre.jpeg|frame|20px]]</pre> [[File:Faltre.jpeg|frame|20px]] <pre>[[File:Fuzzy logic temperature te.svg|frame]]</pre> [[File:Fuzzy logic temperature te.svg|Fuzzy logic temperature te.svg|frame]] === Logic Editing tricks === * Formulas with Unicode: (α → β) → α * Formulas with LaTeX tags: <pre><math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math></pre> <math> \forall x \exists y P(x,y) \nVdash \exists y \forall x P(x,y) </math> 95c953c345cfef02dc21052f01a78fe501b89cef Assinatura de primeira ordem 0 203 850 2020-08-21T23:16:28Z Jmarcos 3 versão 0 wikitext text/x-wiki * Caso homogêneo * Caso heterogêneo (multi-gênero) == Para reflexão == * == Veja também == * 3ef0bfca7fffd2da0fb491cab4ca680480d6a964 851 850 2020-08-21T23:16:46Z Jmarcos 3 wikitext text/x-wiki * [[Caso homogêneo]] * [[Caso heterogêneo (multi-gênero)]] == Para reflexão == * == Veja também == * b671e1437769918905ca9ed200e4635e3fe6cf30 852 851 2020-08-21T23:18:57Z Jmarcos 3 wikitext text/x-wiki * Caso homogêneo * Caso heterogêneo (multi-gênero) == Para reflexão == * == Veja também == * 4bcb83e7ea4061fba2a1a0ac729755fa9e299c46 853 852 2020-08-22T00:47:05Z Jmarcos 3 wikitext text/x-wiki * Caso homogêneo [VIDEO] * Caso heterogêneo (multi-gênero) [AGUARDE!] == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] 3b556830917ac0578c5f5da357850fafe9e4c410 861 853 2020-08-22T01:13:01Z Jmarcos 3 wikitext text/x-wiki * Caso homogêneo [VIDEO] * Caso heterogêneo (multi-gênero) [AGUARDE!] == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * 5c3ca331fc520c46134e9434a9338dd790ab9f65 878 861 2020-08-22T19:13:39Z Jmarcos 3 wikitext text/x-wiki * Caso homogêneo [VIDEO] * Caso heterogêneo (multi-gênero) [AGUARDE!] == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 46af2936844d343647b0bdc1be54d38778f8b66a Termos de primeira ordem 0 204 855 2020-08-22T00:49:03Z Jmarcos 3 Created page with "[VIDEO]" wikitext text/x-wiki [VIDEO] 08d5ded184a744e00b566f86deedd474ab5986a5 856 855 2020-08-22T00:49:32Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * bdd99ce15324d1f99784c41b93d67e6ab207b911 862 856 2020-08-22T01:13:14Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * == Links externos == * e009fc8f62a5d2335284806ea8c1199e45a3ab39 863 862 2020-08-22T01:13:43Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * 8812753b842068d9c4864c61e1f031c2500dfc08 Fórmulas de primeira ordem 0 205 857 2020-08-22T00:49:44Z Jmarcos 3 Created page with "[VIDEO] == Para reflexão == * == Veja também == *" wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * bdd99ce15324d1f99784c41b93d67e6ab207b911 864 857 2020-08-22T01:14:17Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * 8812753b842068d9c4864c61e1f031c2500dfc08 Definição recursiva da linguagem proposicional 0 207 859 2020-08-22T00:55:20Z Jmarcos 3 Created page with "* Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * == Veja também == * Sintaxe da Lógica Proposi..." wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * == Veja também == * [[Sintaxe da Lógica Proposicional]] f20c43792998443d324b8c50a246d6c04016e839 867 859 2020-08-22T01:15:40Z Jmarcos 3 wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * == Veja também == * [[Sintaxe da Lógica Proposicional]] == Links externos == * ca8a2bf0f40c0f7e47549fe06398b46de02d367b 880 867 2020-08-22T19:15:28Z Jmarcos 3 wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 5aaa4f57d2e65da4e357b0f0dd5fd1da5381902f 916 880 2020-08-23T00:49:14Z Admin 1 wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * Conjunto indutivamente definido == Veja também == * [[Has parent page::Sintaxe da Lógica Proposicional]] * [[Has parent page::Conjunto indutivamente definido]] * [[Has parent page::Álgebra absolutamente livre]] * [[Has parent page::Álgebra dos termos]] == Links externos == * 9ab4449bc756486f24f0ac32b9519390e10c341a 917 916 2020-08-23T00:56:15Z Admin 1 wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * Conjunto indutivamente definido == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * d06be2fd0a0aebbfb81d71af40e3934ee328c19e Introdução Computacional à Lógica Matemática 0 199 868 809 2020-08-22T18:55:11Z Jmarcos 3 reestruturação da página wikitext text/x-wiki * [[Sintaxe da Lógica de Primeira Ordem]] * [[Sistemas dedutivos para a Lógica Clássica de Primeira Ordem]] * [[Semântica formal para a Lógica Clássica de Primeira Ordem]] * [[Sintaxe da Lógica Proposicional]] * [[Sistemas dedutivos para a Lógica Clássica Proposicional]] * [[Semântica formal para a Lógica Clássica Proposicional]] == Para reflexão == * == Veja também == * == Links externos == * bf75fbeeec7568cdcc0a99125b68a7bbe15f9d89 869 868 2020-08-22T19:06:08Z Jmarcos 3 wikitext text/x-wiki * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == * 931d7113e78e91d8c1564b93b0558a792ee5520a 873 869 2020-08-22T19:08:04Z Jmarcos 3 wikitext text/x-wiki * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] == Para reflexão == * == Veja também == * == Links externos == * 537bf8ade34fd2299d26e56647cfea6a6a2c51f1 901 892 2020-08-22T21:35:03Z Jmarcos 3 wikitext text/x-wiki * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * 547cc90bafa83e6607eae1905fbb1b3334dc0ba0 892 873 2020-08-22T23:11:58Z Jmarcos 3 wikitext text/x-wiki * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] * [[Sintaxe (lógica)]] * [[Sistemas dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * d33712b758aba4a5bfd3bbdc897757e9ad6212e8 Dedução Natural 0 212 885 2020-08-22T21:03:33Z Jmarcos 3 Created page with "* [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == *" wikitext text/x-wiki * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == * 4aa80b8e57d682e832806d5c1dd924d445caf016 889 885 2020-08-22T21:28:17Z Jmarcos 3 wikitext text/x-wiki * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] == Veja também == * == Links externos == * 1ba9390063e617e2532fbee8613f28c4e2fbfb7e 890 889 2020-08-22T21:34:34Z Jmarcos 3 wikitext text/x-wiki * Elementos constitutivos das árvores [VIDEO] * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] == Veja também == * == Links externos == * 7efe2d9b0a94cd3e6f2ad9c4c4d49c8350a47bf3 891 890 2020-08-22T21:35:36Z Jmarcos 3 wikitext text/x-wiki * Elementos constitutivos das árvores de derivação [VIDEO] * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] == Veja também == * == Links externos == * b9cda62528afcf03ae41067a9a73e1871030c1af Modelos (lógica) 0 216 899 2020-08-22T21:29:12Z Jmarcos 3 criando verbete wikitext text/x-wiki == Para reflexão == * == Veja também == * == Links externos == * e5d9a8c05de059b4e07832abdeca3ed7ddd6cb4d Formalismos dedutivos 0 217 902 2020-08-22T21:36:05Z Jmarcos 3 recriando página apagada por equívoco wikitext text/x-wiki [VIDEO] * [[Dedução Natural]] * [[Cálculo de Sequentes]] == Para reflexão == * == Veja também == * == Links externos == * 132713f6e0cb8bc427295a42eb1b5d8efeb03d4d 903 902 2020-08-22T21:37:20Z Jmarcos 3 wikitext text/x-wiki * Estilos [VIDEO] * [[Dedução Natural]] * [[Cálculo de Sequentes]] == Para reflexão == * == Veja também == * == Links externos == * 4cd32bc6741cc3984ce1f000419e3f2e55786a56 Sintaxe (lógica) 0 214 893 2020-08-22T23:13:53Z Jmarcos 3 criando verbete wikitext text/x-wiki * [[Sintaxe da Lógica Proposicional]] * [[Sintaxe da Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == * b904664f23622ea320e7c85ebf3aec8e9a560ec0 smw/schema:Group:Schema properties 112 218 905 2020-08-23T00:39:30Z 127.0.0.1 0 Semantic MediaWiki group import smw/schema application/json { "type": "PROPERTY_GROUP_SCHEMA", "groups": [ { "group_name": "Schema properties", "message_key": "smw-property-group-label-schema-group", "properties": [ "_SCHEMA_TYPE", "_SCHEMA_DEF", "_SCHEMA_DESC", "_SCHEMA_TAG", "_SCHEMA_LINK", "_FORMAT_SCHEMA", "_CONSTRAINT_SCHEMA", "_PROFILE_SCHEMA" ] } ], "tags": [ "group", "property group" ] } db1874786764ef6634ddfc9cb10c19ed78708dc7 MediaWiki:Smw import skos 8 219 906 2020-08-23T00:39:31Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki http://www.w3.org/2004/02/skos/core#|[http://www.w3.org/TR/skos-reference/skos.rdf Simple Knowledge Organization System (SKOS)] altLabel|Type:Monolingual text broader|Type:Annotation URI broaderTransitive|Type:Annotation URI broadMatch|Type:Annotation URI changeNote|Type:Text closeMatch|Type:Annotation URI Collection|Class Concept|Class ConceptScheme|Class definition|Type:Text editorialNote|Type:Text exactMatch|Type:Annotation URI example|Type:Text hasTopConcept|Type:Page hiddenLabel|Type:String historyNote|Type:Text inScheme|Type:Page mappingRelation|Type:Page member|Type:Page memberList|Type:Page narrower|Type:Annotation URI narrowerTransitive|Type:Annotation URI narrowMatch|Type:Annotation URI notation|Type:Text note|Type:Text OrderedCollection|Class prefLabel|Type:String related|Type:Annotation URI relatedMatch|Type:Annotation URI scopeNote|Type:Text semanticRelation|Type:Page topConceptOf|Type:Page [[Category:Imported vocabulary]] 4327e3118f75f756b955108e04693a361d19c2cb MediaWiki:Smw import foaf 8 220 907 2020-08-23T00:39:31Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki http://xmlns.com/foaf/0.1/|[http://www.foaf-project.org/ Friend Of A Friend] name|Type:Text homepage|Type:URL mbox|Type:Email mbox_sha1sum|Type:Text depiction|Type:URL phone|Type:Text Person|Category Organization|Category knows|Type:Page member|Type:Page [[Category:Imported vocabulary]] 2be18fc91e334e0c7f23bea734cdc2a301fd86e8 MediaWiki:Smw import owl 8 221 908 2020-08-23T00:39:31Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki http://www.w3.org/2002/07/owl#|[http://www.w3.org/2002/07/owl Web Ontology Language (OWL)] AllDifferent|Category allValuesFrom|Type:Page AnnotationProperty|Category backwardCompatibleWith|Type:Page cardinality|Type:Number Class|Category comment|Type:Page complementOf|Type:Page DataRange|Category DatatypeProperty|Category DeprecatedClass|Category DeprecatedProperty|Category differentFrom|Type:Page disjointWith|Type:Page distinctMembers|Type:Page equivalentClass|Type:Page equivalentProperty|Type:Page FunctionalProperty|Category hasValue|Type:Page imports|Type:Page incompatibleWith|Type:Page intersectionOf|Type:Page InverseFunctionalProperty|Category inverseOf|Type:Page isDefinedBy|Type:Page label|Type:Page maxCardinality|Type:Number minCardinality|Type:Number Nothing|Category ObjectProperty|Category oneOf|Type:Page onProperty|Type:Page Ontology|Category OntologyProperty|Category owl|Type:Page priorVersion|Type:Page Restriction|Category sameAs|Type:Page seeAlso|Type:Page someValuesFrom|Type:Page SymmetricProperty|Category Thing|Category TransitiveProperty|Category unionOf|Type:Page versionInfo|Type:Page [[Category:Imported vocabulary]] c109cc4c667590611dc35b3d06655129c572809a Property:Foaf:knows 102 222 909 2020-08-23T00:39:31Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki * [[Imported from::foaf:knows]] * [[Property description::A person known by this person (indicating some level of reciprocated interaction between the parties).@en]] [[Category:Imported vocabulary]] {{DISPLAYTITLE:foaf:knows}} e9134ab265b9bc923266ffa2bbcde2b59557202a Property:Foaf:name 102 223 910 2020-08-23T00:39:32Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki * [[Imported from::foaf:name]] * [[Property description::A name for some thing or agent.@en]] [[Category:Imported vocabulary]] {{DISPLAYTITLE:foaf:name}} 2a8b3537cd6d95e741d56a2fe5b824216824c2e3 Property:Foaf:homepage 102 224 911 2020-08-23T00:39:32Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki * [[Imported from::foaf:homepage]] * [[Property description::URL of the homepage of something, which is a general web resource.@en]] [[Category:Imported vocabulary]] {{DISPLAYTITLE:foaf:homepage}} 083058f1760bcc251820336dbe29dee9a38516e6 Property:Owl:differentFrom 102 225 912 2020-08-23T00:39:33Z 127.0.0.1 0 Semantic MediaWiki default vocabulary import wikitext text/x-wiki * [[Imported from::owl:differentFrom]] * [[Property description::The property that determines that two given individuals are different.@en]] [[Category:Imported vocabulary]] {{DISPLAYTITLE:owl:differentFrom}} d706757d4fb8eff4bb5622ea05ea7cab2f3b1a0c Graphs with Mermaid 0 226 919 2020-08-23T01:11:29Z Admin 1 Created page with " == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "https://sandbox.semantic-mediawiki.org/wiki/A" click B "https://sandbox.semantic-m..." wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "https://sandbox.semantic-mediawiki.org/wiki/A" click B "https://sandbox.semantic-mediawiki.org/wiki/B" click C "https://sandbox.semantic-mediawiki.org/wiki/C" click D "https://sandbox.semantic-mediawiki.org/wiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "https://sandbox.semantic-mediawiki.org/wiki/A" click B "https://sandbox.semantic-mediawiki.org/wiki/B" click C "https://sandbox.semantic-mediawiki.org/wiki/C" click D "https://sandbox.semantic-mediawiki.org/wiki/D" }} {{Keywords|Keywords=mermaid;graph}} [[Category:Mermaid examples]] 6c253ed15258a2030f8cb7f99602410594f67d64 920 919 2020-08-23T01:18:31Z Admin 1 wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} {{Keywords|Keywords=mermaid;graph}} [[Category:Mermaid examples]] f9bc3c6f933320680ceedaa074a6454e327dcde0 921 920 2020-08-23T01:35:34Z Admin 1 wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} <pre> {{Keywords|Keywords=mermaid;graph}} [[Category:Mermaid examples]] </pre> f9a4d1886d60ea693e04153babf0bb8c86258fb9 922 921 2020-08-23T01:46:01Z Admin 1 wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} {{Keywords|Keywords=mermaid;graph}} [[Category:Mermaid examples]] 0832f6ba2d15dd3601547e9897714c57bfeccf45 Graphs with Mermaid 0 226 923 922 2020-08-23T01:48:09Z Admin 1 /* Result */ wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} e8175608067430698efbfa576ff6be681f5c8923 924 923 2020-08-23T02:06:37Z Admin 1 /* Result */ wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} [[Category:Mermaid examples]] fe26efc6854d435c32a948415619fece75033be4 925 924 2020-08-23T02:07:31Z Admin 1 /* Result */ wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} [[Category:Mermaid examples]] 18351be6804f59e7868558c405b6b323cac07a63 929 925 2020-08-23T02:29:53Z Admin 1 wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} {{Category |Category=Mermaid examples, Graphs }} 34a0571675824f500aa81661c5c54ef39bec67c7 931 929 2020-08-23T02:46:11Z Admin 1 wikitext text/x-wiki == Syntax == <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> == Result == {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} e8175608067430698efbfa576ff6be681f5c8923 945 931 2020-08-24T00:51:11Z Admin 1 wikitext text/x-wiki ==Syntax== <pre> {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} </pre> ==Result== {{#mermaid:graph TD; A-->B; A-->C; B-->D; C-->D; click A "http://lolita.dimap.ufrn.br/logicwiki/A" click B "http://lolita.dimap.ufrn.br/logicwiki/B" click C "http://lolita.dimap.ufrn.br/logicwiki/C" click D "http://lolita.dimap.ufrn.br/logicwiki/D" }} 5226bdd5ea8dce8ef9cba6feceaeaed8933da773 Definição recursiva da linguagem proposicional 0 207 926 917 2020-08-23T02:15:01Z Admin 1 wikitext text/x-wiki * Como um conjunto indutivamente definido via [[Has parent page::Sintaxe da Lógica Proposicional]] [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * Conjunto indutivamente definido == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 71b84f5e2e5e6b8e0bbfe98e648fca3e978b823f 1003 926 2020-08-27T01:16:45Z Jmarcos 3 wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * e4c7a716f3d80fa2b16726af238b87b487ad8355 Template:Category 10 227 930 2020-08-23T02:32:03Z Admin 1 Created blank page wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Fundamentos Matemáticos da Computação 1 0 80 932 508 2020-08-23T04:25:37Z Admin 1 wikitext text/x-wiki [[Raciocínio Matemático, Indução e Recursão]] [[Contagem]] [[Técnicas Avançadas de Contagem]] [[Somatório e Produtório]] 91fe421d42ef6a29231bb45eaca4540344f563be Fórmulas de primeira ordem 0 205 935 864 2020-08-23T17:10:36Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * Como tirar proveito da estrutura interna das fórmulas atômicas? == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * 0ede71abd2afb8794dcdee57e3376aaa8e971bdd Formalismos dedutivos 0 217 936 903 2020-08-23T22:03:43Z Jmarcos 3 wikitext text/x-wiki * Estilos [VIDEO] * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] == Para reflexão == * == Veja também == * == Links externos == * 078264e6e3e9c49f9e4cdfb931d1337f65b5cf35 937 936 2020-08-23T22:14:19Z Jmarcos 3 wikitext text/x-wiki * Estilos: axiomático, dedução natural, cálculo de sequentes, etc [VIDEO] * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] == Para reflexão == * == Veja também == * == Links externos == * 718685ed85547530f2c160d5339b511a6f9d04ce 938 937 2020-08-23T22:15:05Z Jmarcos 3 wikitext text/x-wiki * Estilos [VIDEO] ** [[Dedução Natural]] ** [[Cálculo de Sequentes]] ** [[Tableaux]] ** [[Método da Resolução]] == Para reflexão == * == Veja também == * == Links externos == * 9c91b195f30b8628ed352ad6a37a046670c9461c 939 938 2020-08-23T22:17:48Z Jmarcos 3 wikitext text/x-wiki * Estilos [VIDEO] ** [[Dedução Natural]] ** [[Cálculo de Sequentes]] ** [[Tableaux]] ** [[Método da Resolução]] == Para reflexão == * == Veja também == * == Links externos == * 3bf90aba5335e6177b2ebe29c1b88b175a2a2e35 940 939 2020-08-23T22:18:50Z Jmarcos 3 wikitext text/x-wiki * Estilos [VIDEO] : [[Dedução Natural]] : [[Cálculo de Sequentes]] : [[Tableaux]] : [[Método da Resolução]] == Para reflexão == * == Veja também == * == Links externos == * 04d6c0f29d05a09ae6319ca64f712fda84778b56 941 940 2020-08-23T22:19:14Z Jmarcos 3 wikitext text/x-wiki * Estilos [VIDEO] :* [[Dedução Natural]] :* [[Cálculo de Sequentes]] :* [[Tableaux]] :* [[Método da Resolução]] == Para reflexão == * == Veja também == * == Links externos == * 7df2e8e9ed49a8c8176564da8bc899f047f1ddee Dedução Natural 0 212 943 891 2020-08-23T22:24:14Z Jmarcos 3 wikitext text/x-wiki * Elementos constitutivos das árvores de derivação em DN [VIDEO] * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] == Veja também == * == Links externos == * d7451d64364c6d1f308d22055f5a0fae9b02bd83 944 943 2020-08-23T22:30:16Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] == Veja também == * == Links externos == * 1268723e5db223c7874fe1a4a6445222318c9bb8 950 944 2020-08-26T00:19:03Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * [[DN para Lógica Clássica Proposicional]] * [[DN para Lógica Clássica de Primeira Ordem]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que um certo sequente ou uma certa regra não é derivável na lógica clássica? * Como demonstrar que um certo sequente ou uma certa regra classicamente derivável não é derivável na lógica intuicionista? == Veja também == * == Links externos == * b93d7e77c949d2ed599b57bb5210409b3f6f8a80 951 950 2020-08-26T00:21:40Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que um certo sequente ou uma certa regra não é derivável na lógica clássica? * Como demonstrar que um certo sequente ou uma certa regra classicamente derivável não é derivável na lógica intuicionista? == Veja também == * == Links externos == * b42c0ca7aff0d81e533229638178c5a6dd25e73e 955 951 2020-08-26T00:27:38Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * == Links externos == * 4aae2be5270d41d5754b59c65786b3fd351b5411 958 955 2020-08-26T17:31:05Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: '''DN''' = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * == Links externos == * 1b5ee42b66cc8f9b5c902b380de07f43ab792bc6 959 958 2020-08-26T17:32:07Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * == Links externos == * 3a9c6dd797528969ddb11127415206864a17bbb4 960 959 2020-08-26T17:50:30Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Links externos == * edb02c2dea0d22a45fbf8b7bea2539d02573ec86 961 960 2020-08-26T17:53:13Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Links externos == * a6d94efdea5f7da784f18fd1a42a7b2cf431f03c 962 961 2020-08-26T17:54:18Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Links externos == * 0bf16fbd67326c8314c5e4e34e75bb99f11e3d9d 963 962 2020-08-26T17:55:01Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Links externos == * 26c94650ccfe2894566f1b50f9e9978c7b328c07 965 963 2020-08-26T20:15:19Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [AGUARDE!] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [VIDEO] * Regras admissíveis [AGUARDE!] * Regras deriváveis [VIDEO] * [[Estratégias de demonstração]] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] == Links externos == * 0005d434a18ebe39e03d1c282bb614f5a17a16e7 994 965 2020-08-27T00:48:29Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de árvores de derivação do tipo DN (árvores rotulada por fórmulas, com descarte de hipóteses) [VIDEO] * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [VIDEO] * Regras admissíveis [AGUARDE!] * Regras deriváveis [VIDEO] * [[Estratégias de demonstração]] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Relação de consequência]] == Links externos == * 2ff76367907b931f7207acdd6c85c53b27b1e54d 1004 994 2020-08-27T19:16:20Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Definição formal de árvores de derivação do tipo DN (árvores rotulada por fórmulas, com descarte de hipóteses) [VIDEO] * Árvores de derivação: manipulando ''fórmulas'' ou ''sequentes''? [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [VIDEO] * Regras admissíveis [AGUARDE!] * Regras deriváveis [VIDEO] * [[Estratégias de demonstração]] * Conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas [VIDEO] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Relação de consequência]] == Links externos == * bba9197a9729c59b7cc3d864a93411e948c1c508 1005 1004 2020-08-27T19:18:00Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * Definição formal de derivação a partir de um sistema de DN [VIDEO] * O conjunto das derivações como indutivamente definido [VIDEO] * Uso de lemas [VIDEO] * Regras admissíveis [AGUARDE!] * Regras deriváveis [VIDEO] * [[Estratégias de demonstração]] * Conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Relação de consequência]] == Links externos == * 244246a342469dc6ad49f6feca6c80536c4db033 1006 1005 2020-08-27T19:21:13Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * Elementos constitutivos das árvores de derivação em DN: nós, folhas, descendentes diretos (e justificativas), raiz [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * [[Estratégias de demonstração]] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Relação de consequência]] == Links externos == * 73bbfc7c488abcf86124e89ab50b87236a1b563d 1007 1006 2020-08-27T19:53:12Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * [[Estratégias de demonstração]] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Relação de consequência]] == Links externos == * 6b426f9031637a6d076279b3c76aaeaed15077a1 1008 1007 2020-08-27T19:57:55Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] == Para reflexão == * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 767f6544bd8693651dc2618425cd6fbafbe839f8 1010 1008 2020-08-27T20:14:43Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 1c93efa538cafe51ab87165bb9c630863ca5840d 1011 1010 2020-08-27T20:20:04Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação para DN: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Da derivabilidade das regras estruturais [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * ceaeea42437a4d5ac6bed21fc2ffc628f292b52a 1012 1011 2020-08-27T20:31:48Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Da derivabilidade das regras estruturais [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * c9482bfed1cb387b4cbbc09772bf4055d5866033 Estratégias de demonstração 0 231 981 2020-08-26T20:54:29Z Jmarcos 3 criando página wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN :: {{#ev:youtube|sLKMkPQFYWI}} :: ''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video? :: ''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto e por contraposição :: {{#ev:youtube|c9MqSNHrJSI}} :: ''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição. :: ''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo :: {{#ev:youtube|w-f04Idz-6M}} :: ''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição. :: ''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto. :: ''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * e923513365e812259d07f0e1ec63369716c5274f 982 981 2020-08-26T20:57:24Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN #: {{#ev:youtube|sLKMkPQFYWI}} #: ''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video? #: ''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto e por contraposição #: {{#ev:youtube|c9MqSNHrJSI}} #: ''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição. #: ''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo #: {{#ev:youtube|w-f04Idz-6M}} #: ''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição. #: ''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto. #: ''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * 46ff1fd2068f2363a11961f73e76b413e3399bc1 983 982 2020-08-26T20:57:51Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN #: {{#ev:youtube|sLKMkPQFYWI}} #: ''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video? #: ''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto e por contraposição #: {{#ev:youtube|c9MqSNHrJSI}} #: ''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição. #: ''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo #: {{#ev:youtube|w-f04Idz-6M}} #: ''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição. #: ''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto. #: ''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática #: [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * a9a88e2faf63e7a68dde5f99e8a05ecd1030a8fb 985 983 2020-08-26T21:02:46Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN <p> {{#ev:youtube|sLKMkPQFYWI}}</p> <p> ''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p> <p> ''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''?</p> # Raciocínio direto e por contraposição #: {{#ev:youtube|c9MqSNHrJSI}} #: ''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição. #: ''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo #: {{#ev:youtube|w-f04Idz-6M}} #: ''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição. #: ''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto. #: ''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática #: [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * fe01f938cda82977bcdba388628059e271631b72 986 985 2020-08-26T21:03:23Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN #: {{#ev:youtube|sLKMkPQFYWI}}</p> #: ''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video? #: ''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto e por contraposição #: {{#ev:youtube|c9MqSNHrJSI}} #: ''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição. #: ''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo #: {{#ev:youtube|w-f04Idz-6M}} #: ''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição. #: ''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto. #: ''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática #: [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * 212d3e88fd7486970bf995e76388520e2be3919c 987 986 2020-08-26T21:27:43Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?<!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto e por contraposição #: {{#ev:youtube|c9MqSNHrJSI}} #: ''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição. #: ''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo #: {{#ev:youtube|w-f04Idz-6M}} #: ''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição. #: ''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto. #: ''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática #: [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * 9d6d520a5830831e42004c5764f8b03e45ff45eb 988 987 2020-08-26T21:29:27Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática #: [AGUARDE!] == Veja também == * [[Dedução Natural]] == Links externos == * a70cc4727eb6e94e9d8e9debe539f0b412069ba5 990 988 2020-08-26T21:30:28Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * 9738e8b45d813edbb89b4600803038e2be858ec9 992 990 2020-08-26T21:34:33Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == # Algumas estratégias, em DN (na forma de árvores rotuladas com fórmulas)<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * 0ff54cb3b83d3e69aed036133878663b64ac5c85 993 992 2020-08-26T21:35:36Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente demonstrar uma sentença que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * 5a0b07896f6d2639e196e9f0cd386d33e25322bb Modelos (lógica) 0 216 995 899 2020-08-27T00:54:42Z Jmarcos 3 wikitext text/x-wiki (This entry is a stub.) == Para reflexão == * == Veja também == * == Links externos == * 696263ef518e25c94bc6f4e897d01b443fe6e378 Fórmulas proposicionais 0 234 1002 2020-08-27T01:14:09Z Jmarcos 3 redirect simples wikitext text/x-wiki #REDIRECT [[Sintaxe da Lógica Proposicional]] 3279f5eb3305125912fb884a92404cc7f5a937d5 Dedução Natural 0 212 1013 1012 2020-08-27T20:32:32Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da DN: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de DN [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de DN [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 5f56f9910dd3ab1122d711cdfb61363f2e23e22d 1014 1013 2020-08-27T20:33:16Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * c0af3ebe25cd7b12a488e5935f03d1c2bcf7eba5 1015 1014 2020-08-27T20:33:36Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[DN para Lógica Proposicional Intuicionista]] * [[DN para Lógica Proposicional Clássica]] * [[DN para Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 9cde67cd27d0dd70df6bcba0c1dfa0e11c6fc6f3 1029 1015 2020-08-28T19:20:45Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Uso de lemas [AGUARDE!] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 78f9de9dc9e3d4c2713344fe88c897f9777d160d 1033 1029 2020-08-29T02:46:32Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Regras admissíveis [AGUARDE!] * Regras deriváveis [AGUARDE!] * Uso de lemas [AGUARDE!] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 631b3a733713c59a034de91e726a23d9ff596297 1034 1033 2020-08-29T02:49:50Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Regras admissíveis e regras deriváveis [AGUARDE!] * Derivabilidade de regras e uso de lemas [VIDEO] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 71ba915d8d5935b70deb073c125073db550488e0 1035 1034 2020-08-29T20:31:56Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Derivabilidade e admissibilidade de regras [VIDEO] * Uso de lemas [VIDEO] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 810aafe5a14a301d0183b8e33120bf879f0c9196 Introdução Computacional à Lógica Matemática 0 199 1016 901 2020-08-28T17:24:07Z Jmarcos 3 wikitext text/x-wiki * [[Relações de Consequência]] * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * 2623aae7840cd98fdc140090e4afc2eed0b5e420 1018 1016 2020-08-28T17:26:32Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * fd1901a35f96fceed42e85dc3ba63735b7c81c92 Relação de consequência 0 236 1019 2020-08-28T17:27:53Z Jmarcos 3 criando página wikitext text/x-wiki * Versão tarskiana, unilateralista [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] == Links externos == * bd13f1b350ce6732e407f80f6c78411b590e1333 1020 1019 2020-08-28T17:35:42Z Jmarcos 3 wikitext text/x-wiki * Versão tarskiana, unilateralista [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') == Links externos == * 36ece7dce305dcc4f7461532b692d834ead6926e 1021 1020 2020-08-28T19:03:28Z Jmarcos 3 wikitext text/x-wiki * Definição de relação de consequência: versão tarskiana, unilateralista [VIDEO] * Noção de equivalência lógica [VIDEO] * Noções de inconsistência [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') == Links externos == * f19672d670c5ff0a0c5b57d2f6c52a915944ce58 1041 1021 2020-08-30T05:47:34Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista [VIDEO] * Noção de '''equivalência lógica''' [VIDEO] * Noções de '''inconsistência''' [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') == Links externos == * 4f75af3b0707ff9aa04bf7afaecc33d9e73c056a Formalismos dedutivos 0 217 1022 941 2020-08-28T19:12:34Z Jmarcos 3 wikitext text/x-wiki * Estilos de formalismo dedutivo [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] == Links externos == * 89fa0fb2e09307184d2b911b57dde11a0e413378 1042 1022 2020-08-30T05:47:53Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] == Links externos == * de55437235fb55b3aa9c021065b9d8b7c88e0413 Exercícios de Dedução Natural 0 237 1024 2020-08-28T19:14:56Z Jmarcos 3 criando página wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) == Dedução Natural para a Lógica Proposicional Intuicionista == [AGUARDE!] == Dedução Natural para a Lógica Proposicional Clássica == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math></p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. == Veja também == * [[Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * e7d01b8e891003c7c97404ead75feddd4984bea7 1074 1024 2020-09-02T04:53:36Z Jmarcos 3 wikitext text/x-wiki == Dedução Natural para a Lógica Proposicional Intuicionista == [AGUARDE!] == Dedução Natural para a Lógica Proposicional Clássica == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math></p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. == Veja também == * [[Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * deb9e2196c2f39ce8f43b80528fad18ffb137933 Estratégias de demonstração 0 231 1025 993 2020-08-28T19:16:50Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente demonstrar uma fórmula que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * eaeb2e7124808c5c0799442befd18c7eddfbc490 1026 1025 2020-08-28T19:17:40Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínio direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínio por casos e por redução ao absurdo</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente verificar uma conjectura que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * 421ae13f8537386ad887fff7d6edcd13b3ba05a6 1028 1026 2020-08-28T19:19:28Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Algumas estratégias, em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínios direto (versão proposicional) e por contraposição<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínios (clássicos) por casos e por redução ao absurdo (contradição)</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente verificar uma conjectura que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * a383d47b2d474f971d2f2eb2f19af079fd65d61a Dedução Natural para a Lógica Proposicional Intuicionista 0 238 1030 2020-08-28T19:21:14Z Jmarcos 3 criando página wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Regras para a conjunção [VIDEO] * Regras para a implicação (intuicionista) [VIDEO] * Regras para a disjunção [VIDEO] * Regras para o bottom e para o top [VIDEO] * Regras para a negação intuicionista (primitivas versus derivadas) [VIDEO] * Regras para a bi-implicação intuicionista (primitivas versus derivadas) [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] == Links externos == * f6d0223b139a09e1b226697ae50ad6bb41620f8b 1032 1030 2020-08-28T19:24:36Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Regras para a conjunção [VIDEO] * Regras para a implicação (intuicionista) [VIDEO] * Regras para a disjunção [VIDEO] * Regras para o bottom e para o top [VIDEO] * Regras para a negação intuicionista (primitivas versus derivadas) [VIDEO] * Regras para a bi-implicação intuicionista (primitivas versus derivadas) [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 4cdcb6790fc7743fcd8b0298de562071330fead6 1036 1032 2020-08-30T05:05:26Z Jmarcos 3 wikitext text/x-wiki (Abreviatura: DN = Dedução Natural) * Regras para a conjunção [VIDEO] * Regras para a implicação (intuicionista) [VIDEO] * Regras para a disjunção [VIDEO] * Regras para o bottom e para o top [VIDEO] * Regras para a negação intuicionista (primitivas versus derivadas) [VIDEO] * Regras para a bi-implicação intuicionista (primitivas versus derivadas) [VIDEO] * Congruencialidade: Teorema de Substitutividade de Equivalentes ("replacement") [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * c0bc495cf6a1f7175e28c8ed1fd99891810b70f1 Dedução Natural para a Lógica Proposicional Clássica 0 239 1031 2020-08-28T19:22:17Z Jmarcos 3 criando página wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: ver [[Dedução Natural para a Lógica Proposicional Intuicionista]] * Regras para a negação clássica (e o bottom clássico) [VIDEO] * Efeitos das regras clássicas da negação sobre os demais conectivos [VIDEO] == Para reflexão == * Como capturar o comportamento dedutivo da bi-implicação clássica? * Como encontrar regras para outros conectivos clássicos? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 71ab3171ec44d0afc78f5e8393e1f1b4f830d8f6 1071 1031 2020-09-02T04:44:42Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: ver [[Dedução Natural para a Lógica Proposicional Intuicionista]] * Regras para a negação clássica (e o bottom clássico) [VIDEO] * Efeitos das regras clássicas da negação sobre os demais conectivos [VIDEO] == Para reflexão == * Como capturar o comportamento dedutivo da bi-implicação clássica? * Como encontrar regras para outros conectivos clássicos? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude]] == Links externos == * ae901169b615ca36d159b0e57b2da4e924078715 Definição recursiva da linguagem proposicional 0 207 1043 1003 2020-08-30T06:05:03Z Jmarcos 3 wikitext text/x-wiki * Como um conjunto indutivamente definido [VIDEO] * Como uma álgebra absolutamente livre [VIDEO] == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a constituir um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 0f86514a58c88bc4f9205bfc3187f1e53b2cce84 Modelos (lógica) 0 216 1045 995 2020-08-30T23:03:37Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == * 8e20c75eddc98cca3112600dbd2ec5658dac1ddc 1047 1045 2020-08-30T23:09:27Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana] * [https://en.wikipedia.org/wiki/Heyting_algebra] df9cc2c20335a0111b43360f84b9bd9c5cf9cdd2 1048 1047 2020-08-30T23:16:16Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] * [https://en.wikipedia.org/wiki/Heyting_algebra Álgebra de Heyting] 8d7baf354fe988424e6ff865a66dc59bf46f60c9 1049 1048 2020-08-30T23:29:15Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] * [https://en.wikipedia.org/wiki/Heyting_algebra Álgebra de Heyting] bf5c9b4afb364371f3af11817ed35fe3a5ec28cd 1052 1049 2020-08-31T00:01:40Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * 676c1cd77753934119d8bebf063f8fbb97b223f9 1057 1052 2020-08-31T04:36:51Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, EN: entailment) == Links externos == * 72b16b480cd142cbd236f158010361889eb8a5c1 Semântica formal para a Lógica Proposicional Clássica 0 241 1050 2020-08-30T23:30:37Z Jmarcos 3 Created page with "* == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgeb..." wikitext text/x-wiki * == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 9d81b93beca0f1c56cdab62c82f693864fdd9216 1053 1050 2020-08-31T04:30:38Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 3199c85d11214f2133f13d3363dfc20578edc7c9 1059 1053 2020-09-01T00:11:58Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 82058426d9ad41011969b18532718ca46a22ffba 1060 1059 2020-09-01T00:12:11Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] a5a96c9fbab0fa4a94480322c0ce895d7c327466 1061 1060 2020-09-01T02:53:04Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 0b149dba5b99b4a4c36600599f827ce96b9e47b0 1069 1061 2020-09-01T03:48:39Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] * Consequência semântica para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) * [[Expressibilidade dos operadores clássicos]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] * [https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post] 5486d09d929bb2bdcb34ca35828facf4a8ad3e30 1072 1069 2020-09-02T04:46:20Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] * Consequência semântica para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, entailment) * [[Expressibilidade dos operadores clássicos]] * [[Correção e completude]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] * [https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post] 8a3e67f3b4ff690d22664f7778f31f28fbb009de 1075 1072 2020-09-02T04:54:57Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] * Consequência semântica para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, entailment) * [[Correção e completude]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] * [https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post] f142c70a197e1f24ff2d5f043ddab1289ed58f4f 1078 1075 2020-09-02T04:57:52Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] * Consequência semântica para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, entailment) * [[Correção e completude]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] e70fd4fb34f37615e2d82263cb35da1fb32dfe18 Acarretamento 0 242 1051 2020-08-30T23:31:12Z Jmarcos 3 Created page with "* == Para reflexão == * == Veja também == * == Links externos == *" wikitext text/x-wiki * == Para reflexão == * == Veja também == * == Links externos == * 71213d1f6ea6fb95d768bac01a89fffca33dd505 1066 1051 2020-09-01T03:40:08Z Jmarcos 3 wikitext text/x-wiki * Relação de acarretamento (ou consequência semântica, ou ''entailment'') associada a uma propriedade unária [VIDEO] == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * 5193c8aef74554674a2b49d7dee5ded4949d0066 1067 1066 2020-09-01T03:41:13Z Jmarcos 3 wikitext text/x-wiki * Relação de acarretamento (ou consequência semântica, ou ''entailment'') associada a uma propriedade unária [VIDEO] == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 8e2bd0cb0f46a1d01735125268a2e1ef5b0b60ff 1068 1067 2020-09-01T03:44:17Z Jmarcos 3 wikitext text/x-wiki * Relação de acarretamento (ou consequência semântica, ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais [VIDEO] == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 2e9c775cfaa3f200c03216ae75dd00f1214b1276 1070 1068 2020-09-02T04:42:09Z Jmarcos 3 wikitext text/x-wiki * Relação de acarretamento (ou consequência semântica, ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais [VIDEO] * Acarretamento associado a uma dada semântica [VIDEO] * Da validade de sequentes e da correção de regras [VIDEO] * Da validade de fórmulas [VIDEO] == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 370a36f34cf73b1988ea705dd597464dd57cfa97 Correção e completude 0 243 1073 2020-09-02T04:47:16Z Jmarcos 3 esqueleto do verbete wikitext text/x-wiki * == Para reflexão == * == Veja também == * == Links externos == * 71213d1f6ea6fb95d768bac01a89fffca33dd505 Poder expressivo dos operadores clássicos 0 244 1076 2020-09-02T04:57:10Z Jmarcos 3 criando boneco do verbete para o futuro wikitext text/x-wiki * O conjunto dos operadores clássicos é '''funcionalmente completo''' sobre <math>\{0,1\}</math>, isto é, permite expressar qualquer operador <math>n</math>-ário 2-valorado == Para reflexão == * == Veja também == * == Links externos == * [https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post] c4018a26ad1899d70487c7489d23b52c66450695 1077 1076 2020-09-02T04:57:34Z Jmarcos 3 wikitext text/x-wiki * O conjunto dos operadores clássicos é '''funcionalmente completo''' sobre <math>\{0,1\}</math>, isto é, permite expressar qualquer operador <math>n</math>-ário <math>2</math>-valorado == Para reflexão == * == Veja também == * == Links externos == * [https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post] af07aaa04a239fa8e88003010af5e15ea24faf39 Exercícios de semântica formal para a Lógica Proposicional Clássica 0 245 1079 2020-09-02T04:58:47Z Jmarcos 3 esqueleto da página wikitext text/x-wiki * == Para reflexão == * == Veja também == * == Links externos == * 71213d1f6ea6fb95d768bac01a89fffca33dd505 Exercícios de semântica formal para a Lógica Proposicional Clássica 0 245 1080 1079 2020-09-02T05:00:25Z Jmarcos 3 wikitext text/x-wiki == Validade de sequentes == * == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * b095b41fe1313dc2ebc732c69168b3bdb4bd7efb Semântica formal para a Lógica Proposicional Clássica 0 241 1081 1078 2020-09-03T02:12:43Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] * Consequência semântica para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * Como comprovar que a relação de consequência associada à semântica da Lógica Clássica é invariante por substituição? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, entailment) * [[Correção e completude]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 95e8e68154fec36e3701c10606208ba3eac7c69f 1083 1081 2020-09-03T02:16:01Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica [VIDEO] * Semântica de valorações para a Lógica Proposicional Clássica [VIDEO] * O algoritmo das tabelas de verdade [VIDEO] * Consequência semântica para a Lógica Proposicional Clássica [VIDEO] * Congruencialidade: Teorema de Substitutividade de Equivalentes ("replacement") [VIDEO] == Para reflexão == * Como comprovar que a relação de consequência associada à semântica da Lógica Clássica é invariante por substituição? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, entailment) * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 29f8ba5e3ed91ee1d9cf66eda2f4022a6ab08d6c 1099 1083 2020-09-03T23:58:23Z Jmarcos 3 wikitext text/x-wiki * Interpretações boolianas para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * Semântica de valorações para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das tabelas de verdade<!-- --><p>[VIDEO]</p> * Consequência semântica para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * Congruencialidade: Teorema de Substitutividade de Equivalentes ("replacement"), pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é invariante por substituição? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, entailment) * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] c75571b8fc2a9486f24bf3b364389fcd328e6c7f 1102 1099 2020-09-04T00:02:20Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * Interpretações boolianas para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * Semântica de valorações para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das tabelas de verdade<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Consequência semântica para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * Congruencialidade: Teorema de Substitutividade de Equivalentes ("replacement"), pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é invariante por substituição? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, entailment) * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 830b7a89e39c0b4a25d2fc3bf558c161166e4d46 1109 1102 2020-09-04T00:16:59Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Teorema de Substitutividade de Equivalentes ("replacement"), pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] caf2a94ac8ae1dc85cb95972b7c93ee1e70de8b6 1120 1109 2020-09-04T00:29:58Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Teorema de Substitutividade de Equivalentes (''replacement''), pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 215607bee026546218f552d0bb943ce1461332ef 1133 1120 2020-09-04T01:00:19Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] b7cd62f33ceb1cc05add071dcc3ea11554d64a00 Dedução Natural para a Lógica Proposicional Intuicionista 0 238 1082 1036 2020-09-03T02:13:40Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção [VIDEO] * Regras para a implicação (intuicionista) [VIDEO] * Regras para a disjunção [VIDEO] * Regras para o bottom e para o top [VIDEO] * Regras para a negação intuicionista (primitivas versus derivadas) [VIDEO] * Regras para a bi-implicação intuicionista (primitivas versus derivadas) [VIDEO] * Congruencialidade: Teorema de Substitutividade de Equivalentes ("replacement") [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 82504711a47fbe82fb3836ddb4b95f7924726189 1098 1082 2020-09-03T23:56:56Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção<!-- --><p>[VIDEO]</p> * Regras para a implicação (intuicionista)<!-- --><p>[VIDEO]</p> * Regras para a disjunção<!-- --><p>[VIDEO]</p> * Regras para o bottom e para o top<!-- --><p>[VIDEO]</p> * Regras para a negação intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a bi-implicação intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Congruencialidade: Teorema de Substitutividade de Equivalentes ("replacement"), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 0c2cde2c49f160eea77f760e33a6b7252324bc6c 1115 1098 2020-09-04T00:27:11Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>[VIDEO]</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>[VIDEO]</p> * Regras para a '''disjunção'''<!-- --><p>[VIDEO]</p> * Regras para o '''bottom''' e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes ("replacement"), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 82f7098ccee467a6a8c2341303eb3fbb7a3d341d 1116 1115 2020-09-04T00:27:30Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>[VIDEO]</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>[VIDEO]</p> * Regras para a '''disjunção'''<!-- --><p>[VIDEO]</p> * Regras para o '''bottom''' e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes ("''replacement''"), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * bcb02c0c0d0af839ec6ead75dea312fdc7b6aac0 1123 1116 2020-09-04T00:53:19Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>[VIDEO]</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>[VIDEO]</p> * Regras para a '''disjunção'''<!-- --><p>[VIDEO]</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes ("''replacement''"), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * a141b3ab58fd7d0cf4d778dfb544c826054393a5 1132 1123 2020-09-04T00:59:11Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>[VIDEO]</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>[VIDEO]</p> * Regras para a '''disjunção'''<!-- --><p>[VIDEO]</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 3421dd1603312696ac8de9110c5aaca6f4f736a5 Correção e completude 0 243 1085 1073 2020-09-03T02:19:00Z Jmarcos 3 wikitext text/x-wiki * Definições gerais [VIDEO] * Correção e completude de um sistema de Dedução Natural para a Lógica Proposicional Clássica [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Acarretamento]] == Links externos == * ffa95c345e03db8517d4dc080c0b12ce5f360abc 1086 1085 2020-09-03T23:17:06Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 3eb67c5981ec4e387a86978ba92c353354d1620e Correção e completude para a Lógica Proposicional Clássica 0 246 1087 2020-09-03T23:18:44Z Jmarcos 3 criando verbete wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * 21b262842b7c4de58139a31e6db54ae383121b82 1091 1087 2020-09-03T23:50:40Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * Como você poderia usar o meta-teorema de completude para demonstrar a finitariedade (compacidade) da relação de acarretamento associada à semântica da Lógica Proposicional Clássica? == Veja também == * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * d7ea75b57dc5537ad06c9078e4b16d5a372016da 1124 1091 2020-09-04T00:53:57Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * Como você poderia usar o meta-teorema de completude para demonstrar a finitariedade (compacidade) da relação de acarretamento associada à semântica da Lógica Proposicional Clássica? == Veja também == * [[Correção e completude]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * 08ad2e52ec15c137b3f7e815b7b898eb2869bc0f 1125 1124 2020-09-04T00:54:15Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * Como você poderia usar o meta-teorema de completude para demonstrar a finitariedade (''compacidade'') da relação de acarretamento associada à semântica da Lógica Proposicional Clássica? == Veja também == * [[Correção e completude]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * 9a2aee41ba4c24126ff1a17b23a3da9b7cb277e3 Dedução Natural 0 212 1088 1035 2020-09-03T23:20:32Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações [VIDEO] * O conjunto das derivações de um sistema de Dedução Natural [VIDEO] * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses [VIDEO] * Derivações: manipulando fórmulas ou sequentes? [VIDEO] * Derivabilidade e admissibilidade de regras [VIDEO] * Uso de lemas [VIDEO] * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup> [VIDEO] * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural [VIDEO] == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 651fdfbdb8346df09400e6642cbb468b5a6062db 1100 1088 2020-09-03T23:59:04Z Jmarcos 3 wikitext text/x-wiki * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações<!-- --><p>[VIDEO]</p> * O conjunto das derivações de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>[VIDEO]</p> * Derivações: manipulando fórmulas ou sequentes?<!-- --><p>[VIDEO]</p> * Derivabilidade e admissibilidade de regras<!-- --><p>[VIDEO]</p> * Uso de lemas<!-- --><p>[VIDEO]</p> * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 9372d8c0d67eb8ab21c3ffd09b3474d098e586e5 1101 1100 2020-09-04T00:00:22Z Jmarcos 3 wikitext text/x-wiki == Derivações == * Componentes do formalismo dedutivo da Dedução Natural: regras e derivações<!-- --><p>[VIDEO]</p> * O conjunto das derivações de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> * Notação DN<sup>Tree</sup>: derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>[VIDEO]</p> * Derivações: manipulando fórmulas ou sequentes?<!-- --><p>[VIDEO]</p> == Derivabilidade e admissibilidade == * Derivabilidade e admissibilidade de regras<!-- --><p>[VIDEO]</p> * Uso de lemas<!-- --><p>[VIDEO]</p> * Da derivabilidade das regras estruturais na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da relação de consequência dedutiva a partir de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o significado lógico e o uso matemático da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) não é derivável na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * c70414cd5346a68ce3594591c51a484e694bcf02 1112 1101 2020-09-04T00:23:27Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras e derivações<!-- --><p>[VIDEO]</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> * Notação '''DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>[VIDEO]</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>[VIDEO]</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>[VIDEO]</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * b3ea4afd359a483d1d59f5cbce4636225181eaee 1113 1112 2020-09-04T00:23:52Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras e derivações<!-- --><p>[VIDEO]</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>[VIDEO]</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>[VIDEO]</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>[VIDEO]</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 0793d23db7b03472ea36bf61e9093580bd1c3232 Relação de consequência 0 236 1089 1041 2020-09-03T23:20:54Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista [VIDEO] * Noção de '''equivalência lógica''' [VIDEO] * Noções de '''inconsistência''' [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * * [[Correção e completude]] == Links externos == * afd9b3e5cf280ccbeac6f2aeaf663a0088c399f9 1090 1089 2020-09-03T23:21:13Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista [VIDEO] * Noção de '''equivalência lógica''' [VIDEO] * Noções de '''inconsistência''' [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * 222f876853cba199ff4dec395bf6f3c80c5511a6 1107 1090 2020-09-04T00:09:14Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>[VIDEO]</p> * Noção de '''equivalência lógica'''<!-- --><p>[VIDEO]</p> * Noções de '''inconsistência'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * 7ed85718f2cae38f6b6b9a1d602ee680a50347dd Definição recursiva da linguagem proposicional 0 207 1092 1043 2020-09-03T23:51:57Z Jmarcos 3 wikitext text/x-wiki * Como um conjunto indutivamente definido<!-- -->[VIDEO] * Como uma álgebra absolutamente livre<!-- -->[VIDEO] == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 39171f4796a6d25d0dc782bb67ed3e0e587e39ed 1093 1092 2020-09-03T23:52:32Z Jmarcos 3 wikitext text/x-wiki * Como um conjunto indutivamente definido<!-- --><p>[VIDEO]</p> * Como uma álgebra absolutamente livre<!-- --><p>[VIDEO]</p> == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * bd0be6b3eac90bb493dc61211770570e61b8ed8d 1129 1093 2020-09-04T00:56:48Z Jmarcos 3 wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>[VIDEO]</p> * Como uma ''álgebra absolutamente livre''<!-- --><p>[VIDEO]</p> == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 6e14e7058e337277ce20425748b2ed0c18e971ea 1130 1129 2020-09-04T00:57:25Z Jmarcos 3 wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>[VIDEO]</p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'')<!-- --><p>[VIDEO]</p> == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 71f0563e52927bfa9f3fc414f7fbcafef5a8b503 1131 1130 2020-09-04T00:58:00Z Jmarcos 3 wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>[VIDEO]</p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'' da categoria associada)<!-- --><p>[VIDEO]</p> == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 9ace5f08ef70d2c8043e4d9d96b3f0b0278f802d Assinatura de primeira ordem 0 203 1095 878 2020-09-03T23:54:01Z Jmarcos 3 wikitext text/x-wiki * Caso homogêneo<!-- --><p>[VIDEO]</p> * Caso heterogêneo (multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 122481eaebdc4c4b342ea590957fe5c9826086b4 1126 1095 2020-09-04T00:55:17Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de objetos)<!-- --><p>[VIDEO]</p> * Caso ''heterogêneo'' (multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] a208fe89f51b7913c66026c7a17a3a8ce02c5df1 1127 1126 2020-09-04T00:55:37Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de objetos)<!-- --><p>[VIDEO]</p> * Caso ''heterogêneo'' (ou multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 927a0d3c512da6778a44cc43b218c757c78a756d 1135 1127 2020-09-10T01:55:46Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de objetos)<!-- --><p>{{#ev:youtube|kQAkqVJplI4}}</p> * Caso ''heterogêneo'' (ou multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 35522a6c1676d321c886dcdc1d6251ed832160f8 Dedução Natural para a Lógica Proposicional Clássica 0 239 1096 1071 2020-09-03T23:54:57Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: ver [[Dedução Natural para a Lógica Proposicional Intuicionista]] * Regras para a negação clássica (e o bottom clássico)<!-- --><p>[VIDEO]</p> * Efeitos das regras clássicas da negação sobre os demais conectivos<!-- --><p>[VIDEO]</p> == Para reflexão == * Como capturar o comportamento dedutivo da bi-implicação clássica? * Como encontrar regras para outros conectivos clássicos? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude]] == Links externos == * 8e989c9e1545ae607a607b1d0a6be7c901176bb3 1097 1096 2020-09-03T23:55:22Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a negação clássica (e o bottom clássico)<!-- --><p>[VIDEO]</p> * Efeitos das regras clássicas da negação sobre os demais conectivos<!-- --><p>[VIDEO]</p> == Para reflexão == * Como capturar o comportamento dedutivo da bi-implicação clássica? * Como encontrar regras para outros conectivos clássicos? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude]] == Links externos == * 06e46c76366c1a1b629a7e5040f705d908647a8c 1122 1097 2020-09-04T00:52:50Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' (e o '''bottom clássico''')<!-- --><p>[VIDEO]</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>[VIDEO]</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude]] == Links externos == * 377f738582031a28fb9b92d714dd564e5b0ba970 Acarretamento 0 242 1103 1070 2020-09-04T00:03:10Z Jmarcos 3 wikitext text/x-wiki * Relação de acarretamento (ou consequência semântica, ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>[VIDEO]</p> * Acarretamento associado a uma dada semântica<!-- --><p>[VIDEO]</p> * Da validade de sequentes e da correção de regras<!-- --><p>[VIDEO]</p> * Da validade de fórmulas<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 9afd0e0c85c6654feb0d0a25a84f333b1f4f7dbc 1119 1103 2020-09-04T00:29:30Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>[VIDEO]</p> * Acarretamento ''associado a uma dada semântica''<!-- --><p>[VIDEO]</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>[VIDEO]</p> * Da '''validade de fórmulas'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 7a0d8bd205a8d841e59691b66e665aa152af81e6 Formalismos dedutivos 0 217 1105 1042 2020-09-04T00:08:01Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>[VIDEO]</p> == Para reflexão == * ''Prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] == Links externos == * d170e04a11a79d2b2e28d59d4e26221235326de0 1106 1105 2020-09-04T00:08:48Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>[VIDEO]</p> == Para reflexão == * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] == Links externos == * 103ffb2963de4f02ec30e9970a5a4ad557fce39f Poder expressivo dos operadores clássicos 0 244 1110 1077 2020-09-04T00:17:52Z Jmarcos 3 wikitext text/x-wiki * O conjunto dos operadores clássicos é '''funcionalmente completo''' sobre <math>\{0,1\}</math>, isto é, permite expressar qualquer operador <math>n</math>-ário <math>2</math>-valorado<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * == Links externos == * [https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post] a857dc514209415674ba5c4fcca3c5293f258b56 1111 1110 2020-09-04T00:19:27Z Jmarcos 3 wikitext text/x-wiki * O conjunto dos operadores clássicos é '''funcionalmente completo''' sobre <math>\{0,1\}</math>, isto é, permite expressar qualquer operador <math>n</math>-ário <math>2</math>-valorado<!-- --><p>[AGUARDE!]</p> == Para reflexão == * De que forma poderíamos caracterizar o poder expressivo dos operadores ''intuicionistas''? == Veja também == * == Links externos == * Dos fragmentos da Lógica Proposicional Clássica:<!-- --><p>[https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post]</p> ad29a7f7a657faae95784359c387a1d456338c6e 1121 1111 2020-09-04T00:30:21Z Jmarcos 3 wikitext text/x-wiki * O conjunto dos operadores clássicos é '''funcionalmente completo''' sobre <math>\{0,1\}</math>, isto é, permite expressar qualquer operador <math>n</math>-ário <math>2</math>-valorado<!-- --><p>[AGUARDE!]</p> == Para reflexão == * De que forma poderíamos caracterizar o poder expressivo dos operadores ''intuicionistas''? == Veja também == * == Links externos == * Dos ''fragmentos'' da Lógica Proposicional Clássica:<!-- --><p>[https://en.wikipedia.org/wiki/Post%27s_lattice Reticulado de Post]</p> a52902d50a0d0c841968adbd57f09d13b0aa6ca6 Estratégias de demonstração 0 231 1114 1028 2020-09-04T00:25:20Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Algumas '''estratégias''', em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínios '''direto''' (versão proposicional) e '''por contraposição'''<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínios (clássicos) '''por casos''' e '''por redução ao absurdo''' (ou "por contradição")</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente verificar uma conjectura que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] == Links externos == * 79ef12b086586f5e1bea616b0f4a189bb838a239 Modelos (lógica) 0 216 1118 1057 2020-09-04T00:28:09Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, EN: ''entailment'') == Links externos == * 215a67779675ecf52778fd0f039f24f0b108c165 Termos de primeira ordem 0 204 1136 863 2020-09-11T00:24:28Z Jmarcos 3 adicionando video wikitext text/x-wiki {{#ev:youtube|oYTu3emKwvg}} == Para reflexão == * == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * c741c8b8130993e37256e4f3c2f2c7f12f86dd70 Termos de primeira ordem 0 204 1137 1136 2020-09-11T00:44:44Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|MJ89nVrP2GM}} == Para reflexão == * == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * c860220782dd060bc3272e4ee4e07fd284f01d66 Fórmulas de primeira ordem 0 205 1138 935 2020-09-11T04:23:48Z Jmarcos 3 acrescentando video wikitext text/x-wiki {{#ev:youtube|1kkAm0E7CzA}} == Para reflexão == * Como tirar proveito da estrutura interna das fórmulas atômicas? == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * a420142b5bff6aacbe16763c08a13a6f19115940 Definição recursiva da linguagem proposicional 0 207 1141 1131 2020-09-12T01:34:53Z Jmarcos 3 adicionando video wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>{{#ev:youtube|hCCVtDR4RVo}} </p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'' da categoria associada)<!-- --><p>[VIDEO]</p> == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * 0374956a789b6e9caf1bfaa148fca06872329816 1142 1141 2020-09-12T02:39:25Z Jmarcos 3 adicionando video wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>{{#ev:youtube|hCCVtDR4RVo}}</p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'' da categoria associada)<!-- --><p>{{#ev:youtube|7-wpBM93SyY}}</p> == Para reflexão == * Como seria a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * c07ff4f5f5b8323e1ec04a49144cdb602bad6f95 Formalismos dedutivos 0 217 1143 1106 2020-09-12T19:36:45Z Jmarcos 3 acrescentando video wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> == Para reflexão == * É possível ''mecanizar'' inteiramente a Matemática? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] == Links externos == * [[https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel]] e8088e1043de28dd3ec27a7a922f2be6a659cd5d 1144 1143 2020-09-12T19:37:06Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> == Para reflexão == * É possível ''mecanizar'' inteiramente a Matemática? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 3c3359e2b96ee97cd95992743344c7d7d768d8f7 Dedução Natural 0 212 1145 1113 2020-09-13T00:21:25Z Jmarcos 3 adicionando video wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>[VIDEO]</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>[VIDEO]</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>[VIDEO]</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * b26c589565f77f0c843e09c21b5ce11bb7c09621 Dedução Natural para a Lógica Proposicional Intuicionista 0 238 1146 1132 2020-09-13T04:23:12Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>[VIDEO]</p> * Regras para a '''disjunção'''<!-- --><p>[VIDEO]</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * ba702778f195dd4ee617f28d46b81a3cf3b8a6e2 1165 1146 2020-09-13T21:53:30Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>[VIDEO]</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 5df70dab62dfbab3309958f4e3203348de7ef8c3 1166 1165 2020-09-14T04:35:28Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>[VIDEO]</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 27a5dc71d29825e1663d857837b337421118f675 1167 1166 2020-09-14T05:19:50Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * ccbf9365ff9105564a2388af5a4a6434e34d56c6 1168 1167 2020-09-14T08:07:45Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 2f6100cdd4707024060be1920c68b1aca0b28989 1178 1168 2020-09-15T07:51:12Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 8381b9fc8510800d30e0c29ceb70323dd15b075d Exercícios de Dedução Natural 0 237 1147 1074 2020-09-13T20:21:55Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math></p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. #<math>\varphi \land \psi \vdash \psi \land \varphi</math> --><p>{{#ev:youtube|moh07B8dv2k?start=491}}</p><!-- ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 8f60bd7cf4e9b0c510ac8d72ac1004f5492f986c 1148 1147 2020-09-13T20:22:21Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math></p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. #<math>\varphi \land \psi \vdash \psi \land \varphi</math> --><p>{{#ev:youtube|moh07B8dv2k}}</p><!-- ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * e9cb5ecf0fc40c14862a2f5d1d1405d91b9af638 1149 1148 2020-09-13T20:24:24Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math></p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. # <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 44f6cc68a740032ed703d8b3589ba48d1062b707 1150 1149 2020-09-13T20:31:35Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math></p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 4bd94257f7373e411c18ab1e399b1580afa204f3 1151 1150 2020-09-13T20:36:36Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> </p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|urlargs=491}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 5bb1ad34664da179d1e46657781eeb5a12749b59 1152 1151 2020-09-13T20:37:32Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> </p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|urlargs="start=491"}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 2b18cb40ed6ce47ae6e2cceba3f2a5573ed56c38 1153 1152 2020-09-13T20:39:37Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> </p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=491}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * f47031d6133f9149e0dbc8aa0299b1dd1b2538ab 1154 1153 2020-09-13T20:40:15Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> </p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 1b6e405d1584ea9d49c47a1f8a94df314fd216c0 1155 1154 2020-09-13T20:41:56Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> </p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=525}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 39b31bc2529f755565463abcf80c57563c680a79 1156 1155 2020-09-13T20:43:32Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> </p><!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 2237ee8812f5e30df44e798b4f74e314756640db 1157 1156 2020-09-13T20:44:46Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> <!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #Comutatividade de <math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * dc856a5ee70991a378abe3ab7d061bdef78ffb75 1158 1157 2020-09-13T20:46:42Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> <!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #<math>\land</math>: <math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> #<math>\land</math>: <math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math> test ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 7cc8dbeb4e7a85f3505995c9da806aebece08c4f 1159 1158 2020-09-13T20:48:20Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math> <!-- --><p>{{#ev:youtube|kNyjuCFUzC8}}</p><!-- --><p>''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math> #<math>\alpha \vdash \alpha \land \alpha</math> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * eb5d3319caa28302f516aa04413da1dc5de21ce2 1160 1159 2020-09-13T20:49:24Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math> #<math>\alpha \vdash \alpha \land \alpha</math> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 8ec0f7cd8e21a66057ad5df5394e300d337c13e9 1161 1160 2020-09-13T20:52:56Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=581}}</p> #<math>\alpha \vdash \alpha \land \alpha</math> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * c304b17280a76966fe2f2376a078515daf61ddad 1162 1161 2020-09-13T20:53:58Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582}}</p> #<math>\alpha \vdash \alpha \land \alpha</math> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * ecddeb87651c1653aa536a560cd8493727a87ea8 1163 1162 2020-09-13T21:02:38Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção.<!-- --> #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\alpha \vdash \alpha \land \alpha</math> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * a0200dd777c0b82ee93298b7039c158284bafbb7 1164 1163 2020-09-13T21:45:21Z Jmarcos 3 mudando as coisas de lugar wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\alpha \vdash \alpha \land \alpha</math> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 11c8dc1bc78879813bdf39642f82e5be3ef90cb9 1169 1164 2020-09-14T14:49:59Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\psi \vdash \psi \land \psi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 5d833a32f94f1354bafaa57fab43c5a170e045db 1170 1169 2020-09-14T14:50:34Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 29b4c23188059ae8857adc26f718bcdc04ac3e4a 1171 1170 2020-09-14T14:55:24Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --> #<math>\vdash \alpha \to \alpha</math><!-- --> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * dca70da2b31e05ff5c877b6cc8bcf10dd3b97dba 1172 1171 2020-09-14T15:06:46Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 408593aaa63a2683b0227120623fec5d301628aa 1173 1172 2020-09-14T15:30:22Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1424}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 16eda69d08c29191fa40253d9122cae14d14db70 1174 1173 2020-09-14T15:31:59Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 535372a21ab8223109700a4ca98148f31ac696a6 1175 1174 2020-09-14T16:20:53Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 0486b4166759b7d8eaa50878d38ecfd054c7b755 1176 1175 2020-09-14T16:24:44Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> #<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> #<math>\alpha, \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> #<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> #<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> #<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\lor\neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=753}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 9261511f401393af77849e7b75a33825f66cb7fd 1177 1176 2020-09-14T16:26:42Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> #<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> #<math>\alpha, \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> #<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> #<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> #<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ==Dedução Natural para a Lógica Proposicional Clássica== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 5688ffb602e2072d45c57243f419529a8683962d 1182 1177 2020-09-15T17:16:39Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === #<math>\varphi \land \psi \vdash \psi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> #<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> #<math>\alpha, \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> #<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> #<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> #<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * b0157a3b85e188279fdb54c7c2a5848f773fcb09 1183 1182 2020-09-15T17:21:41Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> #<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> #<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> #<math>\alpha, \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> #<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> #<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> #<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 77cf0c5f6b22b1d797b8970b3ca56593be8448b3 1184 1183 2020-09-15T18:07:41Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>====<!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> #<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> #<math>\alpha, \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> #<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> #<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> #<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * eef60597804a05a2110dee51b09504e6b44a5c5d 1185 1184 2020-09-15T18:08:16Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> #<math>\varphi \vdash \varphi \land \varphi</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> #<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math><!-- --><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> #<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> #<math>\vdash \alpha \to (\beta \to \alpha)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> #<math>\vdash \alpha \to \alpha</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> #<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> #<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> #<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math><!-- --><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> #<math>\beta \lor (\alpha \land \beta) \vdash \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> #<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> #<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math><!-- --><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> #<math>\alpha \vdash \neg\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> #<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> #<math>\alpha, \neg\alpha \vdash \neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> #<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> #<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> #<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math><!-- --><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 075f893de96a3060862fca6e9b7e115857dc145f 1186 1185 2020-09-15T18:41:23Z Greati 32 /* Dedução Natural para a Lógica Proposicional Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' #Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math><!-- -->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 00c3cf1aa6df941ac6fe24698ab50bcb451ca782 1187 1186 2020-09-16T01:39:34Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 5a61c2cf20721e7b55d5d52cf55304c9fa4198bd 1188 1187 2020-09-16T01:44:03Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==== (DNE) a partir de DNint + (<math>\bot E_{cls}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * b69be387424c0f5a795d9f715c5a5f2278526a0c Dedução Natural para a Lógica Proposicional Clássica 0 239 1179 1122 2020-09-15T07:51:38Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude]] == Links externos == * b3fb99e760d1acf6932b5efd133471e24485910c Fundamentos Matemáticos da Computação 3 0 193 1180 794 2020-09-15T16:41:54Z Jmarcos 3 referência à estrutura da videopédia wikitext text/x-wiki [[Introdução Computacional à Lógica Matemática]] [https://vitorgreati.me/experiments/logicwiki.html Estrutura (constelação) da videopédia] ee0a5a298306c014e24fc4dee0bd6c74fa001936 1181 1180 2020-09-15T16:42:54Z Jmarcos 3 wikitext text/x-wiki '''[[Introdução Computacional à Lógica Matemática]]''' == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Estrutura (constelação) da videopédia] 540f4f8ee59d31a762b3d21b77a4873c9b6251ca Exercícios de Dedução Natural 0 237 1189 1188 2020-09-16T01:45:55Z Greati 32 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de DNint + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 8dece4c8b7daf8c508b3c2f601453cbdabc5d87f 1190 1189 2020-09-16T01:48:15Z Greati 32 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 7bc938f32b2937221dc67b9b84a266010cc8a30b 1191 1190 2020-09-16T01:50:52Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 84b5288f1b9b79ba3846958e18f612a55d9cc5f2 1192 1191 2020-09-16T01:56:24Z Greati 32 /* Dedução Natural para a Lógica Proposicional Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== *''Derivações na forma de árvores rotuladas com fórmulas'' ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 3464ad42ce01e41443c2a47a08e5f1939b4ccf20 1193 1192 2020-09-16T02:19:07Z Greati 32 /* Derivabilidade de sequentes */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 98fb291ab5d2949ab91ab5507c947cba50b8b1df 1194 1193 2020-09-16T02:21:39Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 52264e62c624bf75346a4119073d9a1bf3d849c8 1196 1194 2020-09-16T05:53:32Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * fcce3d318911eed1482a5d6c90c7b4d8c91fe1a7 1206 1196 2020-09-19T05:15:54Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>(\alpha \to \beta) \lor (\beta \to \alpha), via raciocínio por absurdo</math> ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>(\alpha \to \beta) \lor (\beta \to \alpha), via terceiro excluído</math> ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=423}} ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * f4c8158b7b0c3a81262ec927f519ba9902fbb640 1207 1206 2020-09-19T05:18:55Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>(\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>(\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=423}} ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 42631e7197edcf8fce4621a1ea1e71badf61099c 1208 1207 2020-09-19T05:30:28Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=423}} ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 700a1d88f18d1e890ca71ad9e566260a7a02258c 1209 1208 2020-09-19T05:33:38Z Greati 32 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=423}} ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * afcc694f436947e1b0441ba07ddaa615280771b1 1210 1209 2020-09-19T05:36:30Z Greati 32 /* \vdash (\alpha \to \beta) \lor (\beta \to \alpha), via terceiro excluído */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== Nenhum exemplo ainda para esta seção. ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * dbba3547b24bab609d769427026077698c30dd35 1228 1210 2020-09-22T01:15:14Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== [AGUARDE!] ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * c9071f5808684f369f4fc89a54e628cb93ed532c Dedução Natural 0 212 1195 1145 2020-09-16T05:01:37Z Jmarcos 3 acrescentando video wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>[VIDEO]</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 35cf760c70429fff708e9855fd841fc6abe16835 1199 1195 2020-09-17T05:10:26Z Jmarcos 3 acrescentando video wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 62406103ffb831a175a68cf7106c036ff9689de6 1200 1199 2020-09-17T05:11:33Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> == Noção de consequência dedutiva == * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável na lógica intuicionista? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * d551ec154a822f19a16cbf47c20545535db4973a 1201 1200 2020-09-17T05:38:49Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> == Noção de consequência dedutiva == * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma regra admissível em Nat, o que ocorre se você adicionar esta regra ao estoque de regras primitivas de Nat? E por quê regras deriváveis são sempre admissíveis? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * fb433b69023ee46f3c08b34a5c49c023e40d5945 1202 1201 2020-09-17T05:39:53Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> == Noção de consequência dedutiva == * Definição formal da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * a0bac08e7c47eb60a7b0d4df14e696123667e8ab 1203 1202 2020-09-17T05:46:54Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>[VIDEO]</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * c6f025f04cc8545546349892cfff741ac4197f4f 1204 1203 2020-09-18T05:46:54Z Jmarcos 3 acrescentando video wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>[VIDEO]</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 85697cdfba079e0fa2966c228ef7f57d51594b3e 1205 1204 2020-09-19T01:08:06Z Jmarcos 3 adicionando video wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>[VIDEO]</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * e7777cb61eff3067505e5421c05511f6433874bd 1216 1205 2020-09-21T02:24:35Z Jmarcos 3 acrescentando video wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 26539381cdb68b316536805a2761dbfaefd60dc0 Semântica formal para a Lógica de Primeira Ordem Clássica 0 247 1197 2020-09-16T23:01:32Z Jmarcos 3 criando verbete wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''coincidentes a menos de exceções locais''<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para predicados e para sentenças com quantificadores''<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas'', com ênfase no caso das fórmulas quantificadas<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * c90e7d41c7e3ccbfb8680a332191f33dae49173b 1198 1197 2020-09-16T23:02:57Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''coincidentes a menos de exceções locais''<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais''<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 58d563c231a2550bee83f0018dd21001f3ca261f Relação de consequência 0 236 1211 1107 2020-09-20T02:20:52Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>[VIDEO]</p> * Noções de '''inconsistência'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * 6df020cab24d3cd60f2890ace15b075ef58c67b0 1214 1211 2020-09-20T04:09:17Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>[VIDEO]</p> * Noções de '''inconsistência'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * f78e62eb8caceb61c5cd8c7ef828ee49c0a4b34b 1215 1214 2020-09-20T04:13:58Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>[VIDEO]</p> * Noções de '''inconsistência'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * a96ba4c66b9232cd2443a6bb73d82f95e9e37018 1217 1215 2020-09-21T02:49:12Z Jmarcos 3 adicionando video wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>{{#ev:youtube|fw8t7Kju3gI}}</p> * Noções de '''inconsistência'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * c5b743acaeddac8e6cdbb99d6bb249262f72b313 1220 1217 2020-09-21T04:53:38Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>{{#ev:youtube|Et_hGh-XLnM}}</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>{{#ev:youtube|fw8t7Kju3gI}}</p> * Noções de '''inconsistência'''<!-- --><p>[VIDEO]</p> == Para reflexão == * Quando podemos dizer que duas ''teorias'' (em uma mesma linguagem) são logicamente equivalentes? == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * 7c7454446dcb493c69ce8d6995da7b1984583ba7 1221 1220 2020-09-22T00:57:47Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>{{#ev:youtube|Et_hGh-XLnM}}</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>{{#ev:youtube|fw8t7Kju3gI}}</p> * Noções de '''inconsistência'''<!-- --><p>{{#ev:youtube|DaBt0ZFVDtE}}</p> == Para reflexão == * Quando podemos dizer que duas ''teorias'' (em uma mesma linguagem) são logicamente equivalentes? == Veja também == * [[Introdução Computacional à Lógica Matemática]] * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] == Links externos == * 82465ed044b75903c7b325c19b2e02c04decd319 Semântica formal para a Lógica Proposicional Clássica 0 241 1212 1133 2020-09-20T04:06:48Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), verificado pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] ec3b32f84bc6640a66fc338c0074f31996c92692 1213 1212 2020-09-20T04:08:43Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>[VIDEO]</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] f14201ced25935a80aadcb86d0e10ce5b335ad67 1219 1213 2020-09-21T02:54:41Z Jmarcos 3 acrescentando video wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 8655849bf1172ba719b66a1f7a0ba1d250b12751 1230 1219 2020-09-23T02:34:42Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica; noção de '''satisfação'''; e ''classe de modelos'' de uma dada fórmula, ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventuras necessárias à implementação do algoritmo das tabelas de verdade? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 9fd372434040b2e66096d804a6e8abff74cdd19c 1231 1230 2020-09-23T02:35:10Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica; noção de '''satisfação'''; e ''classe de modelos'' de uma dada fórmula, ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>[VIDEO]</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventuras necessárias à implementação do algoritmo das tabelas de verdade? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 3c7e09650ad300093a3f0c0f13497ed0306074bf 1233 1231 2020-09-24T03:39:25Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica; noção de '''satisfação'''; e ''classe de modelos'' de uma dada fórmula, ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventuras necessárias à implementação do algoritmo das tabelas de verdade? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 86e314f600ff77f0c9d917cff29cd6b0907afa6b Dedução Natural para a Lógica Proposicional Intuicionista 0 238 1218 1178 2020-09-21T02:53:15Z Jmarcos 3 acrescentando video wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>{{#ev:youtube|C2S2z-Emw5Y}}</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Noção de equivalência lógica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * c3a33947cc137fcb2240fffcd9c7ae2d12af57ca 1223 1218 2020-09-22T01:07:16Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>{{#ev:youtube|C2S2z-Emw5Y}}</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Relação de consequência]] == Links externos == * 196c8574effcfd138f5e7fdaf608c46af18a4342 1224 1223 2020-09-22T01:08:29Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>{{#ev:youtube|C2S2z-Emw5Y}}</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 792c12115c0456d27cf53b1bf5b22beb69001b71 Modelos (lógica) 0 216 1222 1118 2020-09-22T01:04:15Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica / EN: ''entailment'') == Links externos == * 165c7c22bc9dbc550b7a82a9112408bd5d5f327e Correção e completude 0 243 1225 1086 2020-09-22T01:09:44Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Dedução Natural]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * a371479c6511ca8ae7aaba64e3c183ddfd9554fe Fórmulas de primeira ordem 0 205 1226 1138 2020-09-22T01:10:31Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|1kkAm0E7CzA}} == Para reflexão == * Como tirar proveito da ''estrutura interna'' das fórmulas atômicas? == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * 5d5e5f84f07f3a3f02586aca1700da23abcddc59 Definição recursiva da linguagem proposicional 0 207 1227 1142 2020-09-22T01:11:07Z Jmarcos 3 wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>{{#ev:youtube|hCCVtDR4RVo}}</p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'' da categoria associada)<!-- --><p>{{#ev:youtube|7-wpBM93SyY}}</p> == Para reflexão == * Como deveria ser a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da Lógica Proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] == Links externos == * d526cdf7e13b030d2eaeb50025b94f613407c374 Dedução Natural para a Lógica Proposicional Clássica 0 239 1229 1179 2020-09-23T02:34:12Z Jmarcos 3 wikitext text/x-wiki * Regras para a conjunção, a disjunção, a implicação intuicionista, o bottom, o top, e a negação intuicionista: <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 44ccbf086107aaa3d9857f4948b437f13b7ae22f Acarretamento 0 242 1232 1119 2020-09-24T02:56:59Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>[VIDEO]</p> * Da '''validade de fórmulas'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 2e9651bf5f59f69d632e7831d2ba620695e435db 1234 1232 2020-09-25T07:05:26Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 4c4d3da4335e74575f2e70906aa66df7e0926800 Exercícios de semântica formal para a Lógica Proposicional Clássica 0 245 1235 1080 2020-09-25T18:48:00Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) \RHD q \to \neg p</math> == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * a11a2d58e2645f09d847accd276215dec60ee2a8 1236 1235 2020-09-25T18:48:42Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) \rhd* q \to \neg p</math> == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * e55e7b012e532258021acb6893f68ef246e3a181 1237 1236 2020-09-25T18:49:14Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) \not\vdash q \to \neg p</math> == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 06d899b075dbbe484df83d624f489ea5690c7033 1238 1237 2020-09-25T18:49:34Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) \not\vdash q \to \neg p</math> === == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * b8b2bf86aa945b1e0d9207e78291de0643e570c4 Exercícios de semântica formal para a Lógica Proposicional Clássica 0 245 1239 1238 2020-09-25T19:04:58Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) &blacktriangleright; q \to \neg p</math> === == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 1716382fb442b0d03a10e7733b4800f48c63b967 1240 1239 2020-09-25T19:10:55Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> &blacktriangleright; <math>q \to \neg p</math> === == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 5f299457ff9fc49bd754226af15f4b4faa893371 1241 1240 2020-09-25T19:11:21Z Greati 32 /* \neg (r \leftrightarrow p) &blacktriangleright; q \to \neg p */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▸ <math>q \to \neg p</math> === == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 8623c7e307fad3f9551b9f1322fd77fa2b75adce 1242 1241 2020-09-25T19:17:54Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▸ <math>q \to \neg p</math> === === <math> (p \land q) \to r </math> ▸ <math>q \to \neg p</math> === === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▹ <math>q \to \neg p</math> === == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 39ce552bcf7dd72d54efc03051129cebb3a842d9 1243 1242 2020-09-25T19:19:33Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * b88ff957eb6846c62fd9e6466fddf811587a3ab1 1244 1243 2020-09-25T19:35:21Z Greati 32 /* Validade de sequentes */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 51e8d1280eabc682044f03e3a77f57770cfdc27a 1245 1244 2020-09-25T20:03:37Z Greati 32 wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi / \Gamma_1,\Gamma_2 \vdash \psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=220}} == Para reflexão == * == Veja também == * == Links externos == * e6c2efe96bf68f27dd0796754e8dce900de62415 1246 1245 2020-09-25T20:07:50Z Greati 32 /* \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi / \Gamma_1,\Gamma_2 \vdash \psi */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to E) \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \quad / \quad \Gamma_1,\Gamma_2 \vdash \psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to I) \Gamma_1, \varphi \vdash \psi \quad / \quad \Gamma \vdash \varphi\to\psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * == Links externos == * b8fefbd44011f67ecfc82a151cc19be4185e0c5e 1247 1246 2020-09-25T20:08:27Z Greati 32 /* Correção de regras */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to E) \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \, / \, \Gamma_1,\Gamma_2 \vdash \psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to I) \Gamma_1, \varphi \vdash \psi \, / \, \Gamma \vdash \varphi\to\psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * == Links externos == * 41e10d3239e58e8a65e7b4d499f75da6a56e8ffa 1248 1247 2020-09-25T20:08:57Z Greati 32 /* Correção de regras */ wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to E) \, \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \, / \, \Gamma_1,\Gamma_2 \vdash \psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to I) \, \Gamma, \varphi \vdash \psi \, / \, \Gamma \vdash \varphi\to\psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * == Links externos == * 3238cc474a26a54f36a5e514139ad5c665745af7 1249 1248 2020-09-25T20:09:39Z Greati 32 wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to \mathrm{E}) \, \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \, / \, \Gamma_1,\Gamma_2 \vdash \psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to \mathrm{I}) \, \Gamma, \varphi \vdash \psi \, / \, \Gamma \vdash \varphi\to\psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * == Links externos == * 01d939da3ad06cb6a057f082504690d8de515736 1269 1249 2020-09-26T17:01:50Z Jmarcos 3 wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === <!---->{{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to \mathrm{E}) \, \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \, / \, \Gamma_1,\Gamma_2 \vdash \psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to \mathrm{I}) \, \Gamma, \varphi \vdash \psi \, / \, \Gamma \vdash \varphi\to\psi </math> === <!---->{{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * 188e1612ca1a24154dae731b694380a4ce6bdda7 1280 1269 2020-09-26T19:33:26Z Jmarcos 3 wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === : {{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === : {{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === : {{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to \mathrm{E}) \, \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \, / \, \Gamma_1,\Gamma_2 \vdash \psi </math> === : {{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to \mathrm{I}) \, \Gamma, \varphi \vdash \psi \, / \, \Gamma \vdash \varphi\to\psi </math> === : {{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * afda60ae528bd01bbee84d699edb36e795e9c46d Correção e completude 0 243 1250 1225 2020-09-26T04:15:55Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki {{#ev:youtube|JpHjzsdazfs}} == Para reflexão == * Qual destes resultados pode ser útil na verificação de que um dado sequente ''não'' é derivável? Como? == Veja também == * [[Relação de consequência]] * [[Dedução Natural]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 6833b47587ae8437abd3f5f9de12361f7863b009 1274 1250 2020-09-26T19:13:50Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|JpHjzsdazfs}} == Para reflexão == * Qual destes resultados pode ser útil na verificação de que um dado sequente ''não'' é derivável? Como? * Digamos que uma regra é ''meramente admissível'' em um dado sistema dedutivo se tal regra for admissível mas não derivável. Assumindo a correção deste sistema para uma dada semântica, que tipo de propriedade ligada à noção de satisfação é preservada pelas suas regras meramente admissíveis? == Veja também == * [[Relação de consequência]] * [[Dedução Natural]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 3fba4e705f4e9ec67d80b0d0c8077ecd01f71ecd Semântica formal para a Lógica de Primeira Ordem Clássica 0 247 1251 1198 2020-09-26T05:35:16Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''coincidentes a menos de exceções locais''<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais''<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 7df8f77917319ec994f70f2ea7f6bcc78f900623 1255 1251 2020-09-26T14:22:09Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais''<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a identidade<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * e10ba5603bfc65451ee06446002eaa93188e7189 1267 1255 2020-09-26T16:05:58Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a identidade<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 11146d791ee20647cbbb18dd73000e7084ee143b 1268 1267 2020-09-26T16:06:18Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * f8968502fa703ade1661cd2dece818ace0c4b323 1285 1268 2020-09-28T14:19:27Z Jmarcos 3 acrescentando video wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>[VIDEO]</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * cfb3f3323a9c2fc558ca81b1b61a4b08908f9f3d 1288 1285 2020-09-28T14:57:59Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 3313a953b8625e35c6db6b407831870f0f5dc576 1289 1288 2020-09-28T15:04:14Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * bf2059703b55dc2ea37787d09317e8744269e929 1290 1289 2020-09-28T20:45:11Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>[VIDEO]</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 37e848dd8d3d09275f7ffd7edfdc060844b8a44c Correção e completude para a Lógica Proposicional Clássica 0 246 1252 1125 2020-09-26T05:45:38Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki {{#ev:youtube|0ZYp82eQ-HM}} == Para reflexão == * Como você poderia usar o meta-teorema de completude para demonstrar a finitariedade (''compacidade'') da relação de acarretamento associada à semântica da Lógica Proposicional Clássica? == Veja também == * [[Correção e completude]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Clássica]] == Links externos == * a1d2db9b65e6846d2cb4a01bd9cb2a5022d5a8b2 Quantificadores 0 248 1253 2020-09-26T05:59:50Z Jmarcos 3 criando página wikitext text/x-wiki == De acordo com a semântica clássica == * '''Dualidade''' entre quantificadores<!-- --><p>[VIDEO]</p> * '''Aninhamento''' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores de '''contagem'''<!-- --><p>[VIDEO]</p> * Quantificadores '''relativizados'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * d40d7bc04cc51a8a55b757638056d1ad08900726 1254 1253 2020-09-26T06:00:31Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * ''Dualidade'' entre quantificadores<!-- --><p>[VIDEO]</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 1a44d84e72fc19acda944b5c253cfec9c53f07c9 Assinatura de primeira ordem 0 203 1256 1135 2020-09-26T14:47:32Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de indivíduos)<!-- --><p>{{#ev:youtube|kQAkqVJplI4}}</p> * Caso ''heterogêneo'' (ou multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] cc2e88509e1142bdcb3d3cf50492672aaf7c599d 1284 1256 2020-09-28T14:19:02Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de indivíduos)<!-- --><p>{{#ev:youtube|kQAkqVJplI4}}</p> * Caso ''heterogêneo'' (ou multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 453fa8c00b23fcd31505339ed1e62a0f3824c1e4 1286 1284 2020-09-28T14:20:07Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de indivíduos)<!-- --><p>{{#ev:youtube|kQAkqVJplI4}}</p> * Caso ''heterogêneo'' (ou multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 03e08b6ae0844984c6fdced77db7efb1e4378f5f Termos de primeira ordem 0 204 1257 1137 2020-09-26T14:49:25Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo''<!-- --><p>{{#ev:youtube|MJ89nVrP2GM}}</p> * Caso ''heterogêneo''<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * 0a75dbf57d272397f946e7db3fd10b33fe1bd1cd Fórmulas de primeira ordem 0 205 1258 1226 2020-09-26T14:49:48Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo''<!-- --><p>{{#ev:youtube|1kkAm0E7CzA}}</p> * Caso ''heterogêneo''<!-- --><p>[AGUARDE!]</p> == Para reflexão == * Como tirar proveito da ''estrutura interna'' das fórmulas atômicas? == Veja também == * [[Assinatura de primeira ordem]] == Links externos == * f93f2b83b0accb5c3f7f7479b6aa20d4b3c609d3 Dedução Natural para a Lógica Proposicional Clássica 0 239 1259 1229 2020-09-26T14:54:27Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * f0429d146e88fab3c82b3a9dfd8feaff667b01ce 1260 1259 2020-09-26T15:01:31Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). O que dizer de interações clássicas entre o bottom e a negação de uma conjunção, ou entre o bottom e a negação de uma implicação? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * bba1f97a39e08f0ddaab64831bd4b2a090f4023f 1261 1260 2020-09-26T15:02:09Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). O que dizer de interações clássicas entre o bottom e a ''negação de uma conjunção'', ou entre o bottom e a ''negação de uma implicação''? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 241a016b4e7567fc2e922f02b9b9154a57fc89f3 1262 1261 2020-09-26T15:51:39Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). O que dizer de interações clássicas entre o bottom e a ''negação de um conectivo <math>\copyright</math>''? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 1f166881edbb25a1cd9e57e8073357a4973e6144 1263 1262 2020-09-26T15:56:10Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). Dado um conectivo ©, o que dizer de interações clássicas entre o bottom e a ''negação de'' ©? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * 4474c8026aa5c86f3ef0dc5405510e3756c59b0b 1265 1263 2020-09-26T16:01:59Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). Dado um conectivo ©, o que o lógico clássico teria a dizer sobre as interações entre o bottom e a ''negação de'' ©? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * cd581338099b5e972d4a9ec39d42764259ac24e2 1266 1265 2020-09-26T16:02:26Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). Em geral, dado um conectivo ©, o que o lógico clássico teria a dizer sobre as interações entre o bottom e a ''negação de'' ©? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * f66e3f04239cc59354187fd1eb2bc0da3bc743f5 Exercícios de Dedução Natural 0 237 1270 1228 2020-09-26T18:15:25Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== [AGUARDE!] ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <!----><p><math> (\bot \mathrm{E}_{cls}) \, \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p> <!----><p>adicionarmos uma regra da forma </p> <!----><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash (\alpha \# \beta) </math></p> <!----><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> <!----><p>adicionarmos a seguinte regra de ''consequentia mirabilis''?</p> <!----><p><math> \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * c2ed5fccfd776ff64424400bf8e90b1e1b85abb9 1271 1270 2020-09-26T18:18:19Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== [AGUARDE!] ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \, \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math><!-- --><p>adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 17b4269ed64ef730e75b78c68b74d5c519e2f726 1272 1271 2020-09-26T18:20:29Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== [AGUARDE!] ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math><!-- --><p>adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p> ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 46116adb54f46ef82b79fa3796bc2cb501cd33cf 1273 1272 2020-09-26T18:24:14Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== [AGUARDE!] ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 53ba9da058b1e4169e2dc1616a64923cbe0b1214 1275 1273 2020-09-26T19:17:42Z Jmarcos 3 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== * Ver raciocínios '''por casos''' e '''por redução ao absurdo''' em [[Estratégias de demonstração]] ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 9970aa416f8f77cb8019037e43b66de87c4a898f 1276 1275 2020-09-26T19:18:32Z Jmarcos 3 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== * Ver raciocínios '''por casos''' e '''por redução ao absurdo''' em [[Estratégias de demonstração]]. ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * c5e37060f1a2d8197787177d58363b3f6e05baa2 1277 1276 2020-09-26T19:22:13Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== ====Raciocínios por casos e por redução ao absurdo <!---->Confira [[Estratégias de demonstração]]. ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * f098a12daaf1066c6f6dc8932c60b0f025ea2114 1278 1277 2020-09-26T19:23:18Z Jmarcos 3 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== ====Raciocínios por casos e por redução ao absurdo==== <!---->Confira [[Estratégias de demonstração]]. ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * dd83ce9a39cc49ae4430212e5161dc3ece3fbfdc 1279 1278 2020-09-26T19:31:55Z Jmarcos 3 /* Derivabilidade de regras */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}}</p> ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}}</p> ====<math>\varphi \vdash \varphi \land \varphi</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}}</p> ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== <!----><p>{{#ev:youtube|moh07B8dv2k|||||start=637}}</p> ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=579}}</p> ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=768}}</p> ====<math>\vdash \alpha \to \alpha</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=888}}</p> ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=965}}</p> ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1015}}</p> ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== <!----><p>{{#ev:youtube|mlEYLd56pMg|||||start=1098}}</p> <!----> ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=759}}</p> ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=850}}</p> ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== <!----><p>{{#ev:youtube|yUejpYb2NgI|||||start=1231}}</p> <!-- --> ====<math>\alpha \vdash \neg\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=463}}</p> ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=498}}</p> ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=570}}</p> ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=596}}</p> ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=741}}</p> ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== <!----><p>{{#ev:youtube|-Exorelokdo|||||start=817}}</p> ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== <!---->{{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== <!---->{{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== <!---->{{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== <!---->{{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * aab332463bc7f97005a97b2a417c56cbed27b7c7 1281 1279 2020-09-26T19:41:02Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=1231}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=142&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * eb234b3646ae948a564afd5263e68cf33a88a73f 1282 1281 2020-09-26T19:41:57Z Jmarcos 3 /* Raciocínio por redução ao absurdo: \Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=1231}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 04cc76e5635b5bd6cdcc4b57138443d8ba89f49a 1283 1282 2020-09-26T19:42:49Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=1231}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * 85a182b20c65f4ed1befddc014fa0ab2f25deeec Semântica formal para a Lógica de Primeira Ordem Clássica 0 247 1291 1290 2020-09-28T23:57:29Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 7f421212b8693ea0858c67056fda46312031cb99 1292 1291 2020-09-29T03:36:23Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' induzidas por uma assinatura de primeira ordem<!-- --><p>[VIDEO]</p> * Noção de '''satisfação''' ''para fórmulas quantificadas''<!-- --><p>[VIDEO]</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 59e1c5bedf0eeb028ea0576546a1e2bf4b1241d9 1295 1292 2020-09-30T06:55:41Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 3fc15bab909c49ff7844eff44b3132a1622f689e 1301 1295 2020-10-02T17:11:45Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>[VIDEO]</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 579a939444973d88ca0a2f3aacba76ae61e226c1 1302 1301 2020-10-03T02:00:44Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noção de acarretamento associada == * Versões alternativas de '''consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * c65bf407283869111e3c64d3ada6130a2d285106 1310 1302 2020-10-05T22:31:32Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noção de acarretamento associada == * '''Satisfatibilidade''' vs '''validade''', e '''verdade'''/'''falsidade''' em uma interpretação<!-- --><p>[VIDEO]</p> * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 7d2c769fcb2c528f8d84768c014bf7a45fd0355d 1311 1310 2020-10-06T00:32:24Z Jmarcos 3 /* Noção de acarretamento associada */ wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Satisfatibilidade, validade, verdade, acarretamento == * '''Satisfatibilidade''' vs '''validade''', e '''verdade'''/'''falsidade''' em uma interpretação<!-- --><p>[VIDEO]</p> * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * a67204594d718e29a23afc74d3b29cac5ff51bc5 Semântica formal para a Lógica Proposicional Clássica 0 241 1293 1233 2020-09-29T13:28:42Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica; noção de '''satisfação'''; e ''classe de modelos'' de uma dada fórmula, ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventuras necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] e24ea029bb7915f1d53b1f3312d5ff6fe29b1b85 Introdução Computacional à Lógica Matemática 0 199 1294 1018 2020-09-29T16:55:38Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica Proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] ed7fc93e7451bc4c7888ae5bdf69b5d564351b2a Quantificadores 0 248 1296 1254 2020-10-01T14:43:19Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=5978&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>[VIDEO]</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 4dd9798bc03a548a83144079fb84c637459821d0 1297 1296 2020-10-01T14:45:18Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=597&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>[VIDEO]</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * a633638456721374cebce97d3771bd1fa99bca18 1298 1297 2020-10-01T14:45:45Z Jmarcos 3 /* De acordo com a semântica clássica */ wikitext text/x-wiki == De acordo com a semântica clássica == * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>[VIDEO]</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 70c89f7e6819d4060d449e517d19d8cb8948eec6 1299 1298 2020-10-02T13:55:35Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Também: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores. * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>[VIDEO]</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 8b1be107acc8c5f5044d42180e66bf6fa1b9c27d 1300 1299 2020-10-02T14:25:22Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>[VIDEO]</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 578888b7e0c3412672dd369c013e1442242c1289 1303 1300 2020-10-03T05:22:01Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 98be05cc42b19ed9437abb06072dece1934930b5 1304 1303 2020-10-04T04:34:34Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|ihfZ4wlgFw}}</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 2bf8ae01e22bb1a7b386adf09689472ebc7cc18a 1305 1304 2020-10-04T04:51:11Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 16c406654ca884bc7c9a712700d395f647188a67 1306 1305 2020-10-04T14:53:21Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> == Para reflexão == * Além da dualidade, você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse '''somente''' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 5fdbc1da1145183e80dd61ecf43036c8b28b7a54 1307 1306 2020-10-04T14:53:49Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>[VIDEO]</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> == Para reflexão == * Além da dualidade, você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * a61f50ef8ba8d80495898d89d79da7ea7adfceaa 1308 1307 2020-10-04T20:19:16Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>[VIDEO]</p> == Para reflexão == * Além da dualidade, você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * c2ce5ebbd1b54fec3ecf44ebbe4d1f3704e9d391 1309 1308 2020-10-05T02:55:31Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da dualidade, você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * b885da313be3de4f34157e16671ba1440c6b021a 1340 1309 2020-10-06T15:31:49Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da ''dualidade'', você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * e178717358ec5e71b9ba9641cec18c7481931de8 Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica 0 250 1312 2020-10-06T00:43:12Z Jmarcos 3 criando verbete wikitext text/x-wiki == Validade global de sequentes == * == Validade de sequentes em uma interpretação fixa == * == Correção de regras == * == Para reflexão == * == Veja também == * == Links externos == * 158592178b6e02ed3a1c0a7b52cc0c57c2cf2574 1328 1312 2020-10-06T03:23:52Z Greati 32 /* Correção de regras */ wikitext text/x-wiki == Validade global de sequentes == * == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * 7bb8dc4845101d23c752e2d787a4a8fd72e4fc24 1329 1328 2020-10-06T03:24:43Z Greati 32 /* Regras para a igualdade quando interpretada como a identidade */ wikitext text/x-wiki == Validade global de sequentes == * == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * 1585bc49110a84d43b78af46da36c651f58c271e 1330 1329 2020-10-06T03:51:37Z Greati 32 /* Validade global de sequentes */ wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi \vdash \forall y \exists x. \varphi </math> === == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * 14c5e53a4bfa6331f22a61ae18337abfb8143a38 1331 1330 2020-10-06T03:55:26Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi \vdash \forall y \exists x. \varphi </math> === {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * bced3cf94b930c923770cb127280b480c6194308 1332 1331 2020-10-06T04:01:44Z Greati 32 /* Validade global de sequentes */ wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi \vdash \forall y \exists x. \varphi </math> === {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi \not\vdash \exists x \forall y. \varphi </math> === {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * e88e2b37257b332a3026a4fa739bf327f3d90fdb 1333 1332 2020-10-06T15:15:20Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi ▷ \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi ▶ \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * 0877ffe3a08bc9c4944a4af7b2e64e8d91ded8bb 1334 1333 2020-10-06T15:16:03Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi </math> ▷ <math> \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi </math> ▶ <math> \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * cbb74cab5a5be3ba03c7ffa5dd511e3b61681609 1335 1334 2020-10-06T15:22:18Z Greati 32 /* Validade global de sequentes */ wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi </math> ▷ <math> \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi </math> ▶ <math> \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x : P)\varphi </math> ◁▷ <math> \not(\exists x : P). \varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * d70aa249c9d5061e4d25091b3e387ae9eecaa313 1336 1335 2020-10-06T15:23:10Z Greati 32 /* (\forall x : P)\varphi ◁▷ \not(\exists x : P). \varphi */ wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi </math> ▷ <math> \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi </math> ▶ <math> \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x : P)\varphi </math> ◁▷ <math> \neg(\exists x : P). \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * 4846e68e39ccab4b6462c2c57153cc97037a5fa1 1337 1336 2020-10-06T15:25:20Z Greati 32 /* (\forall x : P)\varphi ◁▷ \neg(\exists x : P). \neg\varphi */ wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi </math> ▷ <math> \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi </math> ▶ <math> \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x : P)\varphi </math> ◁▷ <math> \neg(\exists x : P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * == Links externos == * 0eefebbfde46309796158e749a7b813aab7b17cb Correção e completude para a Lógica de Primeira Ordem Clássica 0 251 1313 2020-10-06T00:44:14Z Jmarcos 3 criando verbete wikitext text/x-wiki [AGUARDE] == Para reflexão == * == Veja também == * == Links externos == * 3bc2ba1a1a4b4d1daafeec53d7c435bbadbbdccf 1326 1313 2020-10-06T01:04:08Z Jmarcos 3 wikitext text/x-wiki [AGUARDE] == Para reflexão == * == Veja também == * == Links externos == * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] dbe3df381bd9017d94547b0edbeeda735a7d4ee0 1327 1326 2020-10-06T01:04:59Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * == Links externos == * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] 5984de09284a26f428de3d8a073ccbed6d61d877 Dedução Natural 0 212 1316 1216 2020-10-06T00:51:11Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] == Links externos == * 4443f4b69b00c3483918d56d54cf75ada0617391 Dedução Natural para a Lógica de Primeira Ordem Intuicionista 0 253 1317 2020-10-06T00:53:02Z Jmarcos 3 criando verbete wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * e8bb1818ca6d1391fedcd8434320826c0b056fe0 1318 1317 2020-10-06T00:53:55Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 1d22a6b9ed6515395976587ba09e08fefb4973e6 1321 1318 2020-10-06T00:59:09Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * b9a6865ba004d7881a6769735863f3f511d21928 1322 1321 2020-10-06T01:00:21Z Jmarcos 3 wikitext text/x-wiki * Regras para a ''quantificação universal'' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a ''quantificação existencial'' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a ''igualdade''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * 9979a4dd99afda5bbde11376a86db65f31f6af1d 1324 1322 2020-10-06T01:01:11Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''igualdade'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * a567e4686a20feb3eb0751fc6ab859362bbb7fd5 1342 1324 2020-10-06T15:35:09Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''igualdade'''<!-- --><p>[VIDEO]</p> == Para reflexão == * Você seria capaz de propor regras primitivas para os ''quantificadores relativizados''? E de derivar a partir destas últimas as regras dos quantificadores usuais? * Você seria capaz de propor regras primitivas para os ''quantificadores de contagem''? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] == Links externos == * fe26732c2529bd38c2449945f6d5a01aa4372cf2 Exercícios de Dedução Natural 0 237 1319 1283 2020-10-06T00:57:47Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=1231}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] ==Links externos== * c956fc03fc0f862ce9f5d23a69ee7e940be46b14 Dedução Natural para a Lógica de Primeira Ordem Clássica 0 254 1320 2020-10-06T00:58:26Z Jmarcos 3 criando verbete wikitext text/x-wiki * Regras para os ''quantificadores'': <!-- --><p>ver [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]]</p> * ''Efeitos das regras clássicas da negação'' sobre os quantificadores<!-- --><p>[VIDEO]</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). Em geral, dado um conectivo ©, o que o lógico clássico teria a dizer sobre as interações entre o bottom e a ''negação de'' ©? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * b41156463cc4ca0b8616e037458b5e3d26850029 1323 1320 2020-10-06T01:00:49Z Jmarcos 3 wikitext text/x-wiki * Regras para os ''quantificadores'' e para a ''igualdade'': <!-- --><p>ver [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]]</p> * ''Efeitos das regras clássicas da negação'' sobre os quantificadores<!-- --><p>[VIDEO]</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). Em geral, dado um conectivo ©, o que o lógico clássico teria a dizer sobre as interações entre o bottom e a ''negação de'' ©? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * f6949568dad9453c83a9c1a12730eb67efc0dc3d 1341 1323 2020-10-06T15:33:33Z Jmarcos 3 wikitext text/x-wiki * Regras para os ''quantificadores'' e para a ''igualdade'': <!-- --><p>ver [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]]</p> * ''Efeitos das regras clássicas da negação'' sobre os quantificadores<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * 0b3557f1e396f1354e38e6191bdbc6c9b295551a Correção e completude 0 243 1325 1274 2020-10-06T01:03:10Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|JpHjzsdazfs}} == Para reflexão == * Qual destes resultados pode ser útil na verificação de que um dado sequente ''não'' é derivável? Como? * Digamos que uma regra é ''meramente admissível'' em um dado sistema dedutivo se tal regra for admissível mas não derivável. Assumindo a correção deste sistema para uma dada semântica, que tipo de propriedade ligada à noção de satisfação é preservada pelas suas regras meramente admissíveis? == Veja também == * [[Relação de consequência]] * [[Dedução Natural]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] == Links externos == * eb2882b5f94045edbeff3b7d255f720bd4a2c9f1 Acarretamento 0 242 1338 1234 2020-10-06T15:27:38Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|???}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 534b1c9a14e6a2cd1bf65d034b35fa56035f4474 1339 1338 2020-10-06T15:28:00Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] == Links externos == * 2505832946fe49e9535c8efda1a2cbb3f855af90 Semântica formal para a Lógica Proposicional Clássica 0 241 1343 1293 2020-10-06T15:44:03Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica; noção de '''satisfação'''; e ''classe de modelos'' de uma dada fórmula, ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventuras necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] afe2ec2b48cbb3f66053848b7c7da047d6e5cb07 1387 1343 2020-10-06T16:18:47Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' para a Lógica Proposicional Clássica; noção de '''satisfação'''; e ''classe de modelos'' de uma dada fórmula, ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventuras necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 2fbaeaa77b98b5d6ca23b80799d4b65046a531db Acarretamento 0 242 1344 1339 2020-10-06T15:44:38Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Relação de compatibilidade]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] == Links externos == * 9baf945b1b0e0fab5a6eeea647a056217261e00a 1345 1344 2020-10-06T15:45:03Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] == Links externos == * 59b4eaf77155e8705571ab22eba8c7bc46d28859 1386 1345 2020-10-06T16:17:41Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * d5c8825625ce253b91341b1344673b5c100a2625 Semântica formal para a lógica proposicional 0 255 1346 2020-10-06T15:46:54Z Jmarcos 3 Created page with "* [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] == Para reflexão == * == Veja também ==..." wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] == Para reflexão == * == Veja também == * [[Acarretamento]] * [[Correção e completude]] == Links externos == * ad858e8fe1c2665ff10ec9f15f13ac96404bf25a 1382 1346 2020-10-06T16:14:38Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] [AGUARDE!] == Para reflexão == * == Veja também == * [[Acarretamento]] * [[Correção e completude]] == Links externos == * 2866a34268df118cb0d635bd503037b50b11b1d1 1385 1382 2020-10-06T16:17:22Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] [AGUARDE!] == Para reflexão == * == Veja também == * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 4ed00f04e350691e46d724e6ff9d00da22e8065a Semântica formal para a lógica de primeira ordem 0 256 1347 2020-10-06T15:47:18Z Jmarcos 3 Created page with "* [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] == Para reflexão == * == Veja ta..." wikitext text/x-wiki * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] == Para reflexão == * == Veja também == * [[Acarretamento]] * [[Correção e completude]] == Links externos == * 13c7ab06dfe62ff0e39937f9aea8404ff4b98cfe 1389 1347 2020-10-06T16:20:18Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] [AGUARDE!] == Para reflexão == * == Veja também == * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 38da33ad701f7374a6569a8ed928dd8c96ed16e8 Quantificadores 0 248 1348 1340 2020-10-06T15:47:59Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da ''dualidade'', você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem]] == Links externos == * 8c1bfb6c93cd3b88d76b60c37de98b8d3f7196b6 1349 1348 2020-10-06T15:48:25Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da ''dualidade'', você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a lógica de primeira ordem]] == Links externos == * f5d1974a83a251648fcc448b417d7d51e93e3c5c Sintaxe da lógica de primeira ordem 0 257 1351 2020-10-06T15:50:26Z Jmarcos 3 Created page with "* [[Assinatura de primeira ordem]] * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Substituição de variáveis por termos]] == Para reflexão == * ==..." wikitext text/x-wiki * [[Assinatura de primeira ordem]] * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Substituição de variáveis por termos]] == Para reflexão == * == Veja também == * [[Sistema dedutivo para a Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica de Primeira Ordem]] * [[Sintaxe da Lógica Proposicional]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] edc1ea26ee100c1bbf76d27675152ad03dd3d6be 1394 1351 2020-10-06T16:24:35Z Jmarcos 3 wikitext text/x-wiki * [[Assinatura de primeira ordem]] * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Substituição de variáveis por termos]] [AGUARDE!] == Para reflexão == * == Veja também == * [[Formalismos dedutivos]] * [[Semântica formal para a lógica de primeira ordem]] * [[Sintaxe da lógica proposicional]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] 3f4539fe273253f7b14222cda4a3d48f270c705d Introdução Computacional à Lógica Matemática 0 199 1354 1294 2020-10-06T15:53:50Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica proposicional]] * [[Lógica de Primeira Ordem]] * [[Lógicas de Ordem Superior]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] 5471bed02e9120302f15160d044f56fc3b60e5df 1356 1354 2020-10-06T15:55:36Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica proposicional]] * [[Lógica de primeira ordem]] * [[Lógicas de ordem superior]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] 8b1f494dd9cf71184b30ad5ae525362b324b7b35 1358 1356 2020-10-06T15:56:21Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica proposicional]] * [[Lógica de primeira ordem]] * [[Lógicas de ordem superior]] [AGUARDE!] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] b3a2ca00f1f05e638476f8953b074e45fa14a2e2 1359 1358 2020-10-06T15:56:38Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica proposicional]] * [[Lógica de primeira ordem]] * [[Lógica de ordem superior]] [AGUARDE!] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] 3dae54336c3a81713536f34fe57b6e105f3239e9 Lógica proposicional 0 259 1355 2020-10-06T15:54:00Z Jmarcos 3 Created page with "* [[Sintaxe da Lógica Proposicional]] * [[Sistemas dedutivos para a Lógica Proposicional]] * [[Semântica formal para a Lógica Proposicional]] == Para reflexão == * Que..." wikitext text/x-wiki * [[Sintaxe da Lógica Proposicional]] * [[Sistemas dedutivos para a Lógica Proposicional]] * [[Semântica formal para a Lógica Proposicional]] == Para reflexão == * Que consequências tem o fato de que as fórmulas atômicas da lógica proposicional ''não possuem estrutura interna''? == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_proposicional Lógica proposicional] a5b79905a6b9ee8885148d0a6ff634b4cfa99a6f 1363 1355 2020-10-06T15:59:50Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Sistemas dedutivos para a Lógica Proposicional]] * [[Semântica formal para a Lógica Proposicional]] == Para reflexão == * Que consequências tem o fato de que as fórmulas atômicas da lógica proposicional ''não possuem estrutura interna''? == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_proposicional Lógica proposicional] eac0833ffa3e6c1063a990318d3fc7084f988485 1364 1363 2020-10-06T16:00:18Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Sistemas dedutivos para a lógica proposicional]] * [[Semântica formal para a Lógica Proposicional]] == Para reflexão == * Que consequências tem o fato de que as fórmulas atômicas da lógica proposicional ''não possuem estrutura interna''? == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_proposicional Lógica proposicional] 82605d79de2bce49b2669b075e16ac1e60234e7a 1375 1364 2020-10-06T16:10:44Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Formalismos dedutivos]] * [[Semântica formal para a Lógica Proposicional]] == Para reflexão == * Que consequências tem o fato de que as fórmulas atômicas da lógica proposicional ''não possuem estrutura interna''? == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_proposicional Lógica proposicional] d26343e9435eb64c2734695a506d6789a77d95c2 1376 1375 2020-10-06T16:11:05Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Formalismos dedutivos]] * [[Semântica formal para a Lógica Proposicional]] == Para reflexão == * Que consequências tem o fato de que as fórmulas atômicas da lógica proposicional ''não possuem estrutura interna''? == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_proposicional Lógica proposicional] 5554acf67a4a433b1e7574afb8220b09beb83e9e 1380 1376 2020-10-06T16:14:06Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Formalismos dedutivos]] * [[Semântica formal para a lógica proposicional]] == Para reflexão == * Que consequências tem o fato de que as fórmulas atômicas da lógica proposicional ''não possuem estrutura interna''? == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_proposicional Lógica proposicional] 604f6b539ea451241d2dd4b8388e3b0152168c2a Lógica de primeira ordem 0 260 1357 2020-10-06T15:55:48Z Jmarcos 3 Created page with "* [[Sintaxe da lógica de primeira ordem]] * [[Sistemas dedutivos para a lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] == Para reflexã..." wikitext text/x-wiki * [[Sintaxe da lógica de primeira ordem]] * [[Sistemas dedutivos para a lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] == Para reflexão == * == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de primeira ordem] 8e8badb74c44679d058f77285cfe8ef2fbcc4397 Sintaxe (lógica) 0 214 1360 893 2020-10-06T15:57:18Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Sintaxe da lógica de primeira ordem]] == Para reflexão == * == Veja também == * == Links externos == * bbfc35dbccd494d502be0299290add708ae10f92 1367 1360 2020-10-06T16:02:03Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica proposicional]] * [[Sintaxe da lógica de primeira ordem]] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * dd13adbc05e0e8b6226163839a8bb2b1f628bb4f Sintaxe da lógica proposicional 0 261 1361 2020-10-06T15:57:45Z Jmarcos 3 Created page with "* '''Fórmulas'''<!-- --><p>{{#ev:youtube|OqcmQGaJjl0}}</p> * Algumas ''comparações'' entre as linguagens proposicional e de primeira ordem<!-- --><p>{{#ev:youtube|NuMzq-9i2..." wikitext text/x-wiki * '''Fórmulas'''<!-- --><p>{{#ev:youtube|OqcmQGaJjl0}}</p> * Algumas ''comparações'' entre as linguagens proposicional e de primeira ordem<!-- --><p>{{#ev:youtube|NuMzq-9i25o}}</p> == Para reflexão == * == Veja também == * [[Definição recursiva da linguagem proposicional]] * [[Sintaxe da Lógica de Primeira Ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] e6c9e9cdcd3b6dea7dc96bca9bd33b6afebcbcec 1362 1361 2020-10-06T15:58:05Z Jmarcos 3 wikitext text/x-wiki * '''Fórmulas'''<!-- --><p>{{#ev:youtube|OqcmQGaJjl0}}</p> * Algumas ''comparações'' entre as linguagens proposicional e de primeira ordem<!-- --><p>{{#ev:youtube|NuMzq-9i25o}}</p> == Para reflexão == * == Veja também == * [[Definição recursiva da linguagem proposicional]] * [[Sintaxe da lógica de primeira ordem]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] 7e827e3bdc28e4ca60f8e7885f68a64ccedc9dce 1368 1362 2020-10-06T16:02:22Z Jmarcos 3 wikitext text/x-wiki * '''Fórmulas'''<!-- --><p>{{#ev:youtube|OqcmQGaJjl0}}</p> * Algumas ''comparações'' entre as linguagens proposicional e de primeira ordem<!-- --><p>{{#ev:youtube|NuMzq-9i25o}}</p> == Para reflexão == * == Veja também == * [[Definição recursiva da linguagem proposicional]] * [[Sintaxe da lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] c929c14b8ac4ac653f4308df1ec98c84bd5153ca Relação de consequência 0 236 1366 1221 2020-10-06T16:01:44Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>{{#ev:youtube|Et_hGh-XLnM}}</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>{{#ev:youtube|fw8t7Kju3gI}}</p> * Noções de '''inconsistência'''<!-- --><p>{{#ev:youtube|DaBt0ZFVDtE}}</p> == Para reflexão == * Quando podemos dizer que duas ''teorias'' (em uma mesma linguagem) são logicamente equivalentes? == Veja também == * [[Dedução Natural]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 31f808ccdb7671269dd9d989d3047702b218871b 1384 1366 2020-10-06T16:16:03Z Jmarcos 3 wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>{{#ev:youtube|Et_hGh-XLnM}}</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>{{#ev:youtube|fw8t7Kju3gI}}</p> * Noções de '''inconsistência'''<!-- --><p>{{#ev:youtube|DaBt0ZFVDtE}}</p> == Para reflexão == * Quando podemos dizer que duas ''teorias'' (em uma mesma linguagem) são logicamente equivalentes? == Veja também == * [[Formalismos dedutivos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * de0891caf994a97405a5b7b84a19f197dd5de2a0 Formalismos dedutivos 0 217 1369 1144 2020-10-06T16:02:42Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> == Para reflexão == * É possível ''mecanizar'' inteiramente a Matemática? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Dedução Natural]] * [[Cálculo de Sequentes]] * [[Tableaux]] * [[Método da Resolução]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 070476a83562c4223d37479ba0a2bd4f8a02c04b 1371 1369 2020-10-06T16:04:43Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> == Para reflexão == * É possível ''mecanizar'' inteiramente a Matemática? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Sistemas dedutivos para a lógica proposicional]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 90b4a15fc313dbc92cfbb834b6f3043ce8679105 1372 1371 2020-10-06T16:07:15Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> * Sistemas dedutivos: : [[Dedução Natural]] : [[Cálculo de Sequentes]] : [[Tableaux]] : [[Método da Resolução]] == Para reflexão == * Será possível ''mecanizar'' inteiramente a Matemática? * Como caracterizar o principal objeto de estudos da '''Teoria das Demonstrações''' (EN: ''Proof Theory'')? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 6d55eafd61845efbc8a410ffee27ff45c7c2898e 1373 1372 2020-10-06T16:08:06Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> * '''Sistemas dedutivos''': :* [[Dedução Natural]] :* [[Cálculo de Sequentes]] :* [[Tableaux]] :* [[Método da Resolução]] == Para reflexão == * Será possível ''mecanizar'' inteiramente a Matemática? * Como caracterizar o principal objeto de estudos da '''Teoria das Demonstrações''' (EN: ''Proof Theory'')? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] c4cbe4317f8def4aadf6e9ff1cc21d8b0358cf04 1374 1373 2020-10-06T16:09:11Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> * '''Sistemas dedutivos''' particulares: :* no estilo da [[Dedução Natural]] :* no estilo do [[Cálculo de Sequentes]] :* no estilo dos [[Tableaux]] :* no estilo do [[Método da Resolução]] == Para reflexão == * Será possível ''mecanizar'' inteiramente a Matemática? * Como caracterizar o principal objeto de estudos da '''Teoria das Demonstrações''' (EN: ''Proof Theory'')? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 0b35f2f656cc018a845d6703d4e2246d90438611 1383 1374 2020-10-06T16:15:06Z Jmarcos 3 wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> * '''Sistemas dedutivos''' particulares: :* no estilo da [[Dedução Natural]] :* no estilo do [[Cálculo de Sequentes]] [AGUARDE!] :* no estilo dos [[Tableaux]] [AGUARDE!] :* no estilo do [[Método da Resolução]] [AGUARDE!] == Para reflexão == * Será possível ''mecanizar'' inteiramente a Matemática? * Como caracterizar o principal objeto de estudos da '''Teoria das Demonstrações''' (EN: ''Proof Theory'')? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 03dbd63d28cebceb106379db2a2a1dab77b3d180 Modelos (lógica) 0 216 1378 1222 2020-10-06T16:13:02Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica / EN: ''entailment'') == Links externos == * ac1f9cabb2bd97f0b825ae6b87409803c3a865af 1379 1378 2020-10-06T16:13:39Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica / EN: ''entailment'') == Links externos == * a1d5ec4068dd6031fa1aef05a23329e0e4257362 1381 1379 2020-10-06T16:14:20Z Jmarcos 3 wikitext text/x-wiki * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica / EN: ''entailment'') * [[Introdução Computacional à Lógica Matemática]] == Links externos == * d4772bd9f6cca3576225311d73468ca71a20c46d Poder expressivo dos operadores clássicos 0 244 1388 1121 2020-10-06T16:19:31Z Jmarcos 3 wikitext text/x-wiki * O conjunto dos operadores clássicos é '''funcionalmente completo''' sobre <math>\{0,1\}</math>, isto é, permite expressar qualquer operador <math>n</math>-ário <math>2</math>-valorado<!-- --><p>[AGUARDE!]</p> == Para reflexão == * De que forma poderíamos caracterizar o poder expressivo dos operadores ''intuicionistas''? == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * Dos ''fragmentos'' da Lógica Proposicional Clássica:[https://en.wikipedia.org/wiki/Post%27s_lattice O reticulado de Post] 4c8399978310ab0b394917e851198c40163f63db Dedução Natural 0 212 1390 1316 2020-10-06T16:20:51Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas), derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 5ad0d4d2b2975314b091b9260fcc19f8d3798759 Dedução Natural para a Lógica Proposicional Intuicionista 0 238 1391 1224 2020-10-06T16:21:30Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>{{#ev:youtube|C2S2z-Emw5Y}}</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * f23cf223cf4f0e68badd3ace4ebd402d19dfc5e7 Dedução Natural para a Lógica Proposicional Clássica 0 239 1392 1266 2020-10-06T16:22:17Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''conjunção''', a '''disjunção''', a '''implicação intuicionista''', o '''bottom intuicionista''', o '''top''', e a '''negação intuicionista''': <!-- --><p>ver [[Dedução Natural para a Lógica Proposicional Intuicionista]]</p> * Regras para a '''negação clássica''' e o '''bottom clássico'''<!-- --><p>{{#ev:youtube|0RiYJ5EinRE}}</p> * ''Efeitos das regras clássicas da negação'' sobre os demais conectivos<!-- --><p>{{#ev:youtube|abE4_iTh_IU}}</p> == Para reflexão == * Como capturar o comportamento dedutivo da ''bi-implicação clássica''? * Como encontrar regras para ''outros conectivos clássicos'' (ou para suas contrapartes intuicionistas)? * A regra do ''bottom clássico'' captura uma interação 'não-conectival' entre o bottom e a negação (clássica). Em geral, dado um conectivo ©, o que o lógico clássico teria a dizer sobre as interações entre o bottom e a ''negação de'' ©? == Veja também == * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * ffdc15b47edf17e1b8ff7664eb3b743bde80c502 Correção e completude para a Lógica Proposicional Clássica 0 246 1393 1252 2020-10-06T16:22:51Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|0ZYp82eQ-HM}} == Para reflexão == * Como você poderia usar o meta-teorema de completude para demonstrar a finitariedade (''compacidade'') da relação de acarretamento associada à semântica da Lógica Proposicional Clássica? == Veja também == * [[Correção e completude]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 984a103e45da322c6083fba5ae6c1b9f6232a6e5 Assinatura de primeira ordem 0 203 1395 1286 2020-10-06T16:25:07Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo'' (um único gênero de indivíduos)<!-- --><p>{{#ev:youtube|kQAkqVJplI4}}</p> * Caso ''heterogêneo'' (ou multi-gênero)<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] 20d161209e4ab7cdd5bf663fb4e92e6ce7e38fdc Termos de primeira ordem 0 204 1396 1257 2020-10-06T16:25:25Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo''<!-- --><p>{{#ev:youtube|MJ89nVrP2GM}}</p> * Caso ''heterogêneo''<!-- --><p>[AGUARDE!]</p> == Para reflexão == * == Veja também == * [[Assinatura de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * db60aa71e5c022cc325718668ede32eceabcbfe1 Fórmulas de primeira ordem 0 205 1397 1258 2020-10-06T16:26:31Z Jmarcos 3 wikitext text/x-wiki * Caso ''homogêneo''<!-- --><p>{{#ev:youtube|1kkAm0E7CzA}}</p> * Caso ''heterogêneo''<!-- --><p>[AGUARDE!]</p> == Para reflexão == * Como tirar proveito da ''estrutura interna'' das fórmulas atômicas? == Veja também == * [[Assinatura de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 01735f911cefc9847fedbe9b103df402005dbf11 Definição recursiva da linguagem proposicional 0 207 1398 1227 2020-10-06T16:28:06Z Jmarcos 3 wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>{{#ev:youtube|hCCVtDR4RVo}}</p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'' da categoria associada)<!-- --><p>{{#ev:youtube|7-wpBM93SyY}}</p> == Para reflexão == * Como deveria ser a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da lógica proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 918176edb5f33f3c93fbb0db9cdfebb64f6ba2cb Correção e completude 0 243 1399 1325 2020-10-06T16:28:43Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|JpHjzsdazfs}} == Para reflexão == * Qual destes resultados pode ser útil na verificação de que um dado sequente ''não'' é derivável? Como? * Digamos que uma regra é ''meramente admissível'' em um dado sistema dedutivo se tal regra for admissível mas não derivável. Assumindo a correção deste sistema para uma dada semântica, que tipo de propriedade ligada à noção de satisfação é preservada pelas suas regras meramente admissíveis? == Veja também == * [[Relação de consequência]] * [[Formalismos dedutivos]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 82425a5210dee1c1364b62cd5622642b34e5cc3a Semântica formal para a lógica proposicional 0 255 1400 1385 2020-10-06T16:35:55Z Jmarcos 3 wikitext text/x-wiki * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] [AGUARDE!] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e1335be6a9e18ed7f969e2668cc9354f2a5982f1 1401 1400 2020-10-06T16:36:21Z Jmarcos 3 wikitext text/x-wiki * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * bdef45cfe38fa61c1004b8d63ca6c8dbcc3685cf Acarretamento 0 242 1402 1386 2020-10-06T16:36:34Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e00d64683b84d1486c03400dace968161e2a3bc2 1403 1402 2020-10-06T16:37:31Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>{{#ev:youtube|}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 571c91200597c17cbb0a285855afbb14314ffdab 1419 1403 2020-10-06T16:51:21Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento ''associado a uma dada semântica proposicional''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos ''associados a uma dada semântica de primeira ordem''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e3bf1839d87a6838161915f0eb27313937407d82 1420 1419 2020-10-06T17:05:48Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento '''associado a uma dada semântica proposicional'''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos '''associados a uma dada semântica de primeira ordem'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 24f84a54f3934561ea4772ce706379c97a2fe3a7 1424 1420 2020-10-06T17:54:14Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento '''associado a uma semântica proposicional'''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos '''associados a uma semântica de primeira ordem'''<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 1842fddaf8219b98f4574fe1fb37215680fc1c20 1435 1424 2020-10-07T18:36:20Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento '''associado a uma semântica proposicional'''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos '''associados a uma semântica de primeira ordem'''<!-- --><p>{{#ev:youtube|8LKRvIX3KkY}}</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * a064753e1966b4c6bf16c26d776bbce17c8dbcc1 1436 1435 2020-10-07T20:10:09Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento '''associado a uma semântica proposicional'''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos '''associados a uma semântica de primeira ordem'''<!-- --><p>{{#ev:youtube|8LKRvIX3KkY}}</p><!-- --><p>Também: Classes globais e locais (relativas a uma estrutura de interpretação fixa) de modelos que satisfazem uma fórmula; validade global e local de um sequente.</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * fe7a9e35014d61684dcdc66f4bd6c6d6bfd6f3e7 1437 1436 2020-10-07T20:10:53Z Jmarcos 3 wikitext text/x-wiki * Relação de '''acarretamento''' (ou '''consequência semântica''', ou ''entailment'') associada a uma propriedade unária, e suas meta-propriedades fundamentais<!-- --><p>{{#ev:youtube|F66kTGCeB-c}}</p> * Acarretamento '''associado a uma semântica proposicional'''<!-- --><p>{{#ev:youtube|ZqQ_tspmQRQ}}</p> * Acarretamentos '''associados a uma semântica de primeira ordem'''<!-- --><p>{{#ev:youtube|8LKRvIX3KkY}}</p><!-- --><p>Também: classes globais e locais (relativas a uma estrutura de interpretação fixa) de modelos que satisfazem uma fórmula; ''validade global'' e ''validade local'' de um sequente.</p> == Para reflexão == * == Veja também == * [[Relação de consequência]] * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * a4dabf9ad4727a9c3037b393c5c123a8cd667aa4 Semântica formal para a lógica de primeira ordem 0 256 1404 1389 2020-10-06T16:37:50Z Jmarcos 3 wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] [AGUARDE!] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * ca1589fdaf71d1ee0fb4159a1a8063e9dc257035 1405 1404 2020-10-06T16:38:14Z Jmarcos 3 wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] [AGUARDE!] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * d644f1c8fb7b6aed50dfc2eaaf2aac6df8ad16f9 1406 1405 2020-10-06T16:38:33Z Jmarcos 3 wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' de fórmulas em uma interpretação de primeira ordem específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * b90c07eab26963b0debd2c6c278b61226e528122 1429 1406 2020-10-06T19:35:48Z Jmarcos 3 wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' de fórmulas em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Da validade global de um sequente, e da validade de um sequente em uma estrutura de interpretação específica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 2fee844f62d2a2b36fb7928cd3c2a685d06661bc 1430 1429 2020-10-06T19:37:16Z Jmarcos 3 wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' ''de fórmulas'' em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Da '''validade''' global ''de um sequente'', e da validade de um sequente em uma estrutura de interpretação específica<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 8a14a42379eacfa57eeba9a5898e1dae9436b0b9 1438 1430 2020-10-07T20:17:19Z Jmarcos 3 wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' ''de fórmulas'' em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Da '''validade''' global ''de um sequente'', e da validade de um sequente em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|8LKRvIX3KkY|||||start=145&end=258&loop=1}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 9c477606f79250af701b767ba1158c9948ffa814 Semântica formal para a Lógica de Primeira Ordem Clássica 0 247 1407 1311 2020-10-06T16:39:05Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas'''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Satisfatibilidade, validade, verdade, acarretamento == * '''Satisfatibilidade''' vs '''validade''', e '''verdade'''/'''falsidade''' em uma interpretação<!-- --><p>[VIDEO]</p> * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 85edc19c767db11d18c52118ddfd8b65ed4c0eb1 1408 1407 2020-10-06T16:39:42Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Satisfatibilidade, validade, verdade, acarretamento == * '''Satisfatibilidade''' vs '''validade''', e '''verdade'''/'''falsidade''' em uma interpretação<!-- --><p>[VIDEO]</p> * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da Lógica de Primeira Ordem]] * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 1f0af3129be1312a8e625c934ee6b30a559eea00 1409 1408 2020-10-06T16:41:34Z Jmarcos 3 wikitext text/x-wiki * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 22dd99fc472c6295aed592701a43cf3d4fda292a 1426 1409 2020-10-06T18:15:53Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * Consequência semântica para a Lógica de Primeira Ordem Clássica<!-- --><p>VIDEO</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * c40f20f29adb33344313f5bc15e87ee311c36c33 1427 1426 2020-10-06T18:16:50Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>VIDEO</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 6dcfe1c92b94168bcfc9676760c127c01e0012da 1428 1427 2020-10-06T18:17:22Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * b1cc9066c10d9b39839a67016df3999fb17876a6 1434 1428 2020-10-06T22:28:52Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>[VIDEO]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * b7296267599edde8dc5d8cde26dd60e771fc1873 1439 1434 2020-10-07T20:19:27Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * a50fa53d9762e235eeeb4d7b8bf8532f013e03c0 1440 1439 2020-10-07T20:34:12Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica:<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 531174acc494270b90a5d7aafd3537f15926cfe1 Quantificadores 0 248 1410 1349 2020-10-06T16:42:13Z Jmarcos 3 wikitext text/x-wiki == De acordo com a semântica clássica == * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da ''dualidade'', você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e29e281cd1fe5ac79553e26e137bdcb4ddf437ce 1425 1410 2020-10-06T17:59:36Z Jmarcos 3 wikitext text/x-wiki * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da ''dualidade'', você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 4d3a720d3ed4f772f5109692010f2ef9ce070ef6 Correção e completude para a Lógica de Primeira Ordem Clássica 0 251 1411 1327 2020-10-06T16:44:15Z Jmarcos 3 wikitext text/x-wiki [VIDEO] == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] ef80c12a7871ca792fe4b3f801e8565ab560d238 Dedução Natural para a Lógica de Primeira Ordem Intuicionista 0 253 1412 1342 2020-10-06T16:44:27Z Jmarcos 3 wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''igualdade'''<!-- --><p>[VIDEO]</p> == Para reflexão == * Você seria capaz de propor regras primitivas para os ''quantificadores relativizados''? E de derivar a partir destas últimas as regras dos quantificadores usuais? * Você seria capaz de propor regras primitivas para os ''quantificadores de contagem''? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 0c6a3d21caabad6b55829c7ab86233c6f11f7f0c Dedução Natural para a Lógica de Primeira Ordem Clássica 0 254 1413 1341 2020-10-06T16:44:48Z Jmarcos 3 wikitext text/x-wiki * Regras para os ''quantificadores'' e para a ''igualdade'': <!-- --><p>ver [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]]</p> * ''Efeitos das regras clássicas da negação'' sobre os quantificadores<!-- --><p>[VIDEO]</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 0e5b0791f1c63a5738af8c6064ba05d79c219975 Estratégias de demonstração 0 231 1414 1114 2020-10-06T16:45:13Z Jmarcos 3 wikitext text/x-wiki == Em Dedução Natural == * ''Derivações na forma de árvores rotuladas com fórmulas'' # Algumas '''estratégias''', em DN<!-- --><p>{{#ev:youtube|sLKMkPQFYWI}}</p><!-- --><p>''Questão:'' Que outras estratégias de demonstração você conhece e que não apareceram no video?</p><!-- --><p>''Questão:'' Como apresentar as estratégias de demonstração de uma forma mais ''estruturada''? # Raciocínios '''direto''' (versão proposicional) e '''por contraposição'''<!-- --><p>{{#ev:youtube|c9MqSNHrJSI}}</p><!-- --><p>''Tarefa:'' Encontre uma fórmula condicional interessante, e demonstre-a primeiro de forma direta e em seguida ofereça uma demonstração alternativa da mesma fórmula raciocinando por contraposição.</p><!-- --><p>''Questão:'' Você consegue conceber exemplos "naturais" de situações em que é mais o uso do raciocínio por Contraposição é mais convidativo ou parece mais útil do que o uso do raciocínio Direto? # Raciocínios (clássicos) '''por casos''' e '''por redução ao absurdo''' (ou "por contradição")</p><!-- --><p>{{#ev:youtube|w-f04Idz-6M}}</p><!-- --><p>''Tarefa:'' Tente verificar uma conjectura que você tenha achado difícil de demonstrar, primeiro raciocinando por casos, e em seguida demonstre-a novamente raciocinando por contradição.</p><!-- --><p>''Questão:'' Você acha que há casos em que uma destas duas formas de raciocínio é mais adequada do que a outra? Dê exemplos disto.</p><!-- --><p>''Questão:'' Você consegue entender bem a diferença entre os raciocínios por Contraposição e por Redução ao Absurdo? Quando lhe parece que seria mais adequado usar cada um deles? # Raciocínio por indução matemática</p><!-- --><p>[AGUARDE!]</p> == Veja também == * [[Dedução Natural]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 71ab08d0b92190dffa6b257b2e77ecfa1fc160fa Exercícios de Dedução Natural 0 237 1415 1319 2020-10-06T16:46:25Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=1231}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== *[[Dedução Natural]] *[[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 8657a13dcf75378eaaa21b7c1cff10f36e8186e1 1416 1415 2020-10-06T16:47:09Z Jmarcos 3 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \lor \beta \not\vdash \alpha \land \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=1231}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 754f29024b9e9088507a7c102cb9ca3c0cd7b694 1443 1416 2020-10-07T20:59:56Z Greati 32 /* Derivabilidade de sequentes */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * c12946022a50068a808e23508dc0a00c85c26b8b Exercícios de semântica formal para a Lógica Proposicional Clássica 0 245 1417 1280 2020-10-06T16:47:45Z Jmarcos 3 wikitext text/x-wiki == Validade de sequentes == === <math>\neg (r \leftrightarrow p) </math> ▶ <math>q \to \neg p</math> === : {{#ev:youtube|sS-teSnboTU|||||start=125}} === <math> (p \land q) \to r </math> ▶ <math>q \to \neg p</math> === : {{#ev:youtube|sS-teSnboTU|||||start=179}} === <math> \neg (r \leftrightarrow p), (p \land q) \to r </math> ▷ <math>q \to \neg p</math> === : {{#ev:youtube|sS-teSnboTU|||||start=210}} == Correção de regras == === <math> (\to \mathrm{E}) \, \Gamma_1 \vdash \varphi \to \psi; \Gamma_2 \vdash \varphi \, / \, \Gamma_1,\Gamma_2 \vdash \psi </math> === : {{#ev:youtube|bIxOFR0elFo|||||start=220}} === <math> (\to \mathrm{I}) \, \Gamma, \varphi \vdash \psi \, / \, \Gamma \vdash \varphi\to\psi </math> === : {{#ev:youtube|bIxOFR0elFo|||||start=348}} == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 54002b929ce43fb8bb27fc09e8f60a7b0f96c013 Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica 0 250 1418 1337 2020-10-06T16:48:29Z Jmarcos 3 /* Veja também */ wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi </math> ▷ <math> \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi </math> ▶ <math> \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x : P)\varphi </math> ◁▷ <math> \neg(\exists x : P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para a igualdade quando interpretada como a identidade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 4282428e8f5464694a81e551b5bb6ddcd02abee9 1431 1418 2020-10-06T19:41:19Z Jmarcos 3 wikitext text/x-wiki == Validade global de sequentes == === <math> \exists x \forall y. \varphi </math> ▷ <math> \forall y \exists x. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> \forall y \exists x. \varphi </math> ▶ <math> \exists x \forall y. \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x : P)\varphi </math> ◁▷ <math> \neg(\exists x : P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 441aa1d06e0683e8176e8d7c2a4990d586bc3856 1432 1431 2020-10-06T19:42:34Z Jmarcos 3 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x : P)\varphi </math> ◁▷ <math> \neg(\exists x : P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 3f199b0601d18dfbe56599d95f5a09be151c1584 1433 1432 2020-10-06T19:59:43Z Jmarcos 3 /* (\forall x : P)\varphi ◁▷ \neg(\exists x : P) \neg\varphi */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == * == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * adc32ef7fba6a56d00555a46f30df9a3b54d9508 1444 1433 2020-10-08T03:04:51Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷ <math> w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 591349a0844999711add039943b533e1fa1512bd 1445 1444 2020-10-08T03:32:05Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math>_{Sem} (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷ <math> w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e702e0d48d2a5e83edd644dd7813bbf79ec8f17b 1446 1445 2020-10-08T03:33:44Z Greati 32 /* (\forall x)\varphi \to (\forall x)\psi ▶ _{Sem} (\forall x)(\varphi \to \psi) */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶<math>_{Sem} (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷ <math> w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 334805bf6b2ed903ee0f84b84ae0f70551d1c4c8 1447 1446 2020-10-08T03:36:12Z Greati 32 /* Validade global de sequentes */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷<math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶<math>_{Sem} (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷ <math> w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 91d196b23305a3bf396a59e2f28ffe90ebbbee66 1448 1447 2020-10-08T03:36:44Z Greati 32 /* (\forall x)\varphi \to (\forall x)\psi ▶_{Sem} (\forall x)(\varphi \to \psi) */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷<math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷ <math> w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e223e70f273823fbef5e50acb926c30e5886cceb Dedução Natural 0 212 1421 1390 2020-10-06T17:08:26Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: regras (primitivas e derivadas / casos particulares: axiomas e teoremas) e derivações<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e07dc3ff3f675db7e52a8b5d8371b15550a3fec1 1422 1421 2020-10-06T17:08:52Z Jmarcos 3 wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: ''regras'' (primitivas e derivadas / casos particulares: axiomas e teoremas) e ''derivações''<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * aabad35aa704aa56394bd83f690ed42689e66248 1441 1422 2020-10-07T20:53:45Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: ''regras'' (primitivas e derivadas / casos particulares: axiomas e teoremas) e ''derivações''<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? * Note que, em geral, os sistemas dedutivos no formalismo da Dedução Natural contêm regras de (''introdução'' e de) ''eliminação''. Não há nada chamado "regras de ''exclusão''"! == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * d5c57aec2088e7e3a73099c98016628a0e98f36b Semântica formal para a Lógica Proposicional Clássica 0 241 1423 1387 2020-10-06T17:16:15Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' e noção de '''satisfação''' para a Lógica Proposicional Clássica; ''classe de modelos que satisfazem'' uma dada fórmula ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventura necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 1a396f6ee1665e78f5331ec2446fd1ba4984bd7a Lógica de primeira ordem 0 260 1442 1357 2020-10-07T20:59:35Z Jmarcos 3 wikitext text/x-wiki * [[Sintaxe da lógica de primeira ordem]] * [[Formalismos dedutivos]] * [[Semântica formal para a lógica de primeira ordem]] == Para reflexão == * == Veja também == * == Links externos == * [https://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de primeira ordem] d84c9cbb8ba4f89756385b119b6d14f3a74412b9 Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica 0 250 1449 1448 2020-10-08T03:40:23Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷ <math> w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 2b572b83e67b31c73ee9b6ef0b101238b138d2cc 1450 1449 2020-10-08T03:43:00Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x f (y g z) </math> ▷<math>^{\mathfrak{I}} w \approx (x f y) g (x f z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 10f1ded576e46dd5ec34c91c2df44389afa7fa41 1471 1450 2020-10-14T02:13:19Z Jmarcos 3 /* Validade de sequentes em uma interpretação fixa */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em uma interpretação fixa == === <math> w \approx x \mathsf{f} (y \mathsf{g} z) </math> ▷<math>^{\mathfrak{I}} w \approx (x \mathsf{f} y) \mathsf{g} (x \mathsf{f} z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 88579205cf1b3f13f9d63464f07a9975c2eb388a 1472 1471 2020-10-14T02:14:00Z Jmarcos 3 /* Validade de sequentes em uma interpretação fixa */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em interpretações particulares == === <math> w \approx x \mathsf{f} (y \mathsf{g} z) </math> ▷<math>^{\mathfrak{I}} w \approx (x \mathsf{f} y) \mathsf{g} (x \mathsf{f} z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * def8247b64eb86cf9f44768ad2d0f48e5d5de496 1488 1472 2020-10-15T23:53:42Z Greati 32 /* Links externos */ wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em interpretações particulares == === <math> w \approx x \mathsf{f} (y \mathsf{g} z) </math> ▷<math>^{\mathfrak{I}} w \approx (x \mathsf{f} y) \mathsf{g} (x \mathsf{f} z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] 6c84a6fa38d693213dbf5b3f3d6ab88fa4bdd565 1495 1488 2020-10-17T13:43:54Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em interpretações particulares == === <math> w \approx x \mathsf{f} (y \mathsf{g} z) </math> ▷<math>^{\mathfrak{I}} w \approx (x \mathsf{f} y) \mathsf{g} (x \mathsf{f} z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} === <math>\Gamma \vdash (\forall x)\varphi / \Gamma \vdash \varphi[x \mapsto t]</math> === : {{#ev:youtube|zRZmfBlAiv0|||||start=490&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] 91fd4e341c586873868ea9a0bec712de2da280c5 1496 1495 2020-10-17T13:48:35Z Greati 32 wikitext text/x-wiki == Validade global de sequentes == === <math> (\exists x)(\forall y) \varphi </math> ▷ <math> (\forall y)(\exists x) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=328&loop=1}} === <math> (\forall y)(\exists x) \varphi </math> ▶ <math> (\exists x)(\forall y) \varphi </math> === : {{#ev:youtube|7ihfZ4wlgFw|||||start=563&loop=1}} === <math> (\forall x{:}P)\varphi </math> ◁▷ <math> \neg(\exists x{:}P) \neg\varphi </math> === : {{#ev:youtube|H3HhormBWr8|||||start=450&loop=1}} ===<math> (\forall x)(\varphi \to \psi) </math> ▷ <math> (\forall x)\varphi \to (\forall x)\psi </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=519&loop=1}} === <math> (\forall x)\varphi \to (\forall x)\psi </math> ▶ <math> (\forall x)(\varphi \to \psi) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=624&loop=1}} == Validade de sequentes em interpretações particulares == === <math> w \approx x \mathsf{f} (y \mathsf{g} z) </math> ▷<math>^{\mathfrak{I}} w \approx (x \mathsf{f} y) \mathsf{g} (x \mathsf{f} z) </math> === : {{#ev:youtube|8LKRvIX3KkY|||||start=1087&loop=1}} == Correção de regras == === Regras para o símbolo de igualdade === : {{#ev:youtube|RTlRlxsRzjA|||||start=239&loop=1}} === <math>\mathrm{(\forall E)} \Gamma \vdash (\forall x)\varphi / \Gamma \vdash \varphi[x \mapsto t]</math> === : {{#ev:youtube|zRZmfBlAiv0|||||start=490&loop=1}} === <math>\mathrm{(\forall I)} \Gamma \vdash \varphi[x \mapsto y] / \Gamma \vdash (\forall x)\varphi </math> === : {{#ev:youtube|zRZmfBlAiv0|||||start=721&loop=1}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] 19a7e88906ae613a6d6bbebc1f0e262b0bd56cc2 Sintaxe da lógica de primeira ordem 0 257 1451 1394 2020-10-09T00:09:10Z Jmarcos 3 wikitext text/x-wiki * [[Assinatura de primeira ordem]] * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] == Para reflexão == * == Veja também == * [[Formalismos dedutivos]] * [[Semântica formal para a lógica de primeira ordem]] * [[Sintaxe da lógica proposicional]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * Para as definições recursivas de ''substituição de variáveis por termos'' (em um termo e em uma fórmula) e para a noção de ''termo livre para variável em fórmula'', consulte as páginas 7 a 13 do capítulo 3 do livro '''DGM''', disponível [https://sites.google.com/site/sequiturquodlibet/courses/lc-dgm neste link]. * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] e7ee5038f6070f7edf851e9e0f9a1c990f00f189 1452 1451 2020-10-09T00:11:07Z Jmarcos 3 wikitext text/x-wiki * [[Assinatura de primeira ordem]] * [[Termos de primeira ordem]] * [[Fórmulas de primeira ordem]] == Para reflexão == * == Veja também == * [[Formalismos dedutivos]] * [[Semântica formal para a lógica de primeira ordem]] * [[Sintaxe da lógica proposicional]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * Para as definições recursivas de ''variáveis livres e ligadas'' em uma dada fórmula, ''substituição de variáveis por termos'' (em um termo e em uma fórmula) e para a noção fundamental de ''termo livre para variável em fórmula'', consulte as páginas 7 a 13 do capítulo 3 do livro '''DGM''', disponível [https://sites.google.com/site/sequiturquodlibet/courses/lc-dgm neste link]. * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] 203fe9f4134b1cd166dad54c9c3acc2f2ce36cb0 Dedução Natural para a Lógica de Primeira Ordem Intuicionista 0 253 1453 1412 2020-10-10T19:58:05Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>{{#ev:youtube|B7fFRZF_wao}}</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>[VIDEO]</p> * Regras para a '''igualdade'''<!-- --><p>[VIDEO]</p> == Para reflexão == * Você seria capaz de propor regras primitivas para os ''quantificadores relativizados''? E de derivar a partir destas últimas as regras dos quantificadores usuais? * Você seria capaz de propor regras primitivas para os ''quantificadores de contagem''? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 2d6d22f01baafc06cdfb08bac3f2aaeca1c93bbb 1460 1453 2020-10-12T13:09:32Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>{{#ev:youtube|B7fFRZF_wao}}</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>{{#ev:youtube|C37Y-1vqRAY}}</p> * Regras para a '''igualdade'''<!-- --><p>[VIDEO]</p> == Para reflexão == * Você seria capaz de propor regras primitivas para os ''quantificadores relativizados''? E de derivar a partir destas últimas as regras dos quantificadores usuais? * Você seria capaz de propor regras primitivas para os ''quantificadores de contagem''? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 17f51d8e5e3c0bbda765a5b99656fd6d81d0462d 1473 1460 2020-10-14T18:57:07Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>{{#ev:youtube|B7fFRZF_wao}}</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>{{#ev:youtube|C37Y-1vqRAY}}</p> * Regras para a '''igualdade'''<!-- --><p>{{#ev:youtube|knltOmL0XEg}}</p> == Para reflexão == * Você seria capaz de propor regras primitivas para os ''quantificadores relativizados''? E de derivar a partir destas últimas as regras dos quantificadores usuais? * Você seria capaz de propor regras primitivas para os ''quantificadores de contagem''? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 9bcb783884b1cad9f34486baaefd2cc70594c266 1492 1473 2020-10-16T00:01:07Z Greati 32 /* Links externos */ wikitext text/x-wiki * Regras para a '''quantificação universal''' intuicionista<!-- --><p>{{#ev:youtube|B7fFRZF_wao}}</p> * Regras para a '''quantificação existencial''' intuicionista<!-- --><p>{{#ev:youtube|C37Y-1vqRAY}}</p> * Regras para a '''igualdade'''<!-- --><p>{{#ev:youtube|knltOmL0XEg}}</p> == Para reflexão == * Você seria capaz de propor regras primitivas para os ''quantificadores relativizados''? E de derivar a partir destas últimas as regras dos quantificadores usuais? * Você seria capaz de propor regras primitivas para os ''quantificadores de contagem''? == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] * [http://pt.wikipedia.org/wiki/Quantifica%C3%A7%C3%A3o Quantificação] * [http://pt.wikipedia.org/wiki/Quantifica%C3%A7%C3%A3o_existencia Quantificação existencial] * [http://pt.wikipedia.org/wiki/Quantifica%C3%A7%C3%A3o_universal Quantificação universal] 12563a94d9b72f3fe2391daef0878c9fb9725860 Exercícios de Dedução Natural 0 237 1454 1443 2020-10-11T00:25:56Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 57fdd39e9b2dd8655e1724eaaf3155260f050e77 1455 1454 2020-10-11T18:06:14Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 766329d4eecea9cedc9bb84bde4c1216f110d5f8 1456 1455 2020-10-11T18:15:06Z Greati 32 /* Dedução Natural para a Lógica de Primeira Ordem Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * e7a5aee9be259c7e2ea0e798b0b7286a61383936 1457 1456 2020-10-11T18:27:21Z Greati 32 /* Derivabilidade de sequentes */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi \land \psi \dashv\vdash (\forall x) \varphi \land (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 34e1bda82ccd97dbdac086dc1c469c22f3186ee9 1458 1457 2020-10-11T18:29:23Z Greati 32 /* (\forall x) \varphi \land \psi \dashv\vdash (\forall x) \varphi \land (\forall x)\psi */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2<math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 88e0234b0430f6fffc40a485027e072c527dea1d 1459 1458 2020-10-11T18:31:26Z Greati 32 /* Dedução Natural para a Lógica de Primeira Ordem Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 1ac00483d27cc999d78f52efe69e5eafeb0afb73 1461 1459 2020-10-12T20:08:54Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)P(x)\toQ(y) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 315b774c928f9bf5437180ad1745a0ff16194740 1462 1461 2020-10-12T20:10:30Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)P(x) \to Q(y) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 226c0fc485cabc966639c80ecb558814f162247c 1463 1462 2020-10-12T20:14:54Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 54b31bf78d8012f98716c8feb1bfb03d1e9a8d46 1465 1463 2020-10-13T01:28:53Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 9ed63d67cd9347a19715f876013ba6aa94fbcb29 1466 1465 2020-10-13T01:34:47Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 42ea9088e1ce5b1b90f4f496c68d6ffcc9fa012f 1467 1466 2020-10-13T01:49:02Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 0059db0c0ec42f710a0dded4a2fb4962bd95213a 1474 1467 2020-10-14T22:17:10Z Greati 32 wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>\neg(\exists x)\neg\varphi \vdash (\forall x)\varphi</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=188}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * 0bcfc21b97320cb193e33f335f514d109e67b350 1475 1474 2020-10-14T22:34:02Z Greati 32 /* Derivabilidade de sequentes */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>\neg(\exists x)\neg\varphi \vdash (\forall x)\varphi</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=188}} ==== <math>\vdash (\exists x)(\forall y)(B(y) \lor \neg B(x))</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=460}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * c23331796d574e3e8c71439b4c1ee544e8470031 1476 1475 2020-10-14T22:52:14Z Greati 32 /* Dedução Natural para a Lógica de Primeira Ordem Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>\neg(\exists x)\neg\varphi \vdash (\forall x)\varphi</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=188}} ==== <math>\vdash (\exists x)(\forall y)(B(y) \lor \neg B(x))</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=460}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==== <math>(\approx_{sim}) \Gamma \vdash t_1 \approx t_2 / \Gamma \vdash t_2 \approx t_1</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=435}} ==== <math>(\approx_{trn}) \Gamma_1 \vdash t_1 \approx t_2; \Gamma_2 \vdash t_2 \approx t_3/ \Gamma_1,\Gamma_2 \vdash t_1 \approx t_3</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=515}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * bd0e40efb54cd38a83fccdd35bb7f175f7b79cd2 1487 1476 2020-10-15T23:51:35Z Greati 32 /* Links externos */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>\neg(\exists x)\neg\varphi \vdash (\forall x)\varphi</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=188}} ==== <math>\vdash (\exists x)(\forall y)(B(y) \lor \neg B(x))</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=460}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==== <math>(\approx_{sim}) \Gamma \vdash t_1 \approx t_2 / \Gamma \vdash t_2 \approx t_1</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=435}} ==== <math>(\approx_{trn}) \Gamma_1 \vdash t_1 \approx t_2; \Gamma_2 \vdash t_2 \approx t_3/ \Gamma_1,\Gamma_2 \vdash t_1 \approx t_3</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=515}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] * [http://pt.wikipedia.org/wiki/Sistema_dedutivo Sistema dedutivo] 671b9566c9990afad18c61e9cf9857fc31034613 Correção e completude 0 243 1464 1399 2020-10-12T20:58:35Z Jmarcos 3 wikitext text/x-wiki {{#ev:youtube|JpHjzsdazfs}} == Para reflexão == * Qual destes resultados pode ser útil na verificação de que um dado sequente ''não'' é derivável? Como? * Digamos que uma regra seja ''meramente admissível'' em um dado sistema dedutivo se tal regra for admissível mas não derivável. Assumindo a correção deste sistema para uma dada semântica, que tipo de propriedade ligada à noção de satisfação é preservada pelas suas regras meramente admissíveis? == Veja também == * [[Relação de consequência]] * [[Formalismos dedutivos]] * [[Acarretamento]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 2675c96533a3722b0d5f7923e7d4c6f4d423408c Introdução Computacional à Lógica Matemática 0 199 1468 1359 2020-10-13T02:27:53Z Jmarcos 3 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica proposicional]] * [[Lógica de primeira ordem]] * [[Lógica de ordem superior]] [AGUARDE!] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] * [https://sites.google.com/site/sequiturquodlibet/courses/laac Lógica Aplicada à Computação]: material de estudos, livro didático, exercícios e exames-tipo dda22f627b399c55db197ec1386dbc8f9e858281 Dedução Natural 0 212 1469 1441 2020-10-13T02:42:51Z Jmarcos 3 /* Para reflexão */ wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: ''regras'' (primitivas e derivadas / casos particulares: axiomas e teoremas) e ''derivações''<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? * Note que, em geral, os sistemas dedutivos no formalismo da Dedução Natural contêm regras de ''introdução'' e de ''eliminação''. Não há nada chamado "regras de ''inclusão''" ou "regras de ''exclusão''"! == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * e0f101b53ea27b0b608338f64a13ef30a86feece 1491 1469 2020-10-15T23:59:11Z Greati 32 /* Links externos */ wikitext text/x-wiki == Derivações == * '''Componentes do formalismo dedutivo''' da Dedução Natural: ''regras'' (primitivas e derivadas / casos particulares: axiomas e teoremas) e ''derivações''<!-- --><p>{{#ev:youtube|By6E_g2xf1Q}}</p> * O '''conjunto das derivações''' de um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|c4gdWh7vBwQ}}</p> * '''Notação DN<sup>Tree</sup>''': derivações como árvores de fórmulas, com descarte de hipóteses<!-- --><p>{{#ev:youtube|UdA2l5BK960}}</p> * Derivações: ''manipulando fórmulas ou sequentes''?<!-- --><p>{{#ev:youtube|TyFDAjvSBgM}}</p> == Derivabilidade e admissibilidade == * '''Derivabilidade''' e '''admissibilidade''' de regras<!-- --><p>{{#ev:youtube|2KzJ3hB9a-Q}}</p> * Uso de '''lemas'''<!-- --><p>{{#ev:youtube|9BdeXjhyJWs}}</p> * Da ''derivabilidade das regras estruturais'' na notação DN<sup>Tree</sup><!-- --><p>{{#ev:youtube|jdHxUb2koy8}}</p> == Noção de consequência dedutiva == * Definição formal e principais meta-propriedades da '''relação de consequência dedutiva''' associada a um sistema de Dedução Natural<!-- --><p>{{#ev:youtube|C9G_kswh-z4}}</p> == Para reflexão == * Qual o ''significado lógico'' e o ''uso matemático'' da regra estrutural (𝕋)? * Como demonstrar que uma certa regra (ou um certo sequente) ''não é derivável'' na lógica clássica? * Como demonstrar que uma certa regra (ou um certo sequente) classicamente derivável não é derivável ''na lógica intuicionista''? * Se R é uma ''regra admissível'' em '''Nat''', o que ocorre se você ''adicionar'' esta regra ao estoque de regras primitivas de '''Nat'''? E por quê ''regras deriváveis são sempre admissíveis''? * Note que, em geral, os sistemas dedutivos no formalismo da Dedução Natural contêm regras de ''introdução'' e de ''eliminação''. Não há nada chamado "regras de ''inclusão''" ou "regras de ''exclusão''"! == Veja também == * [[Dedução Natural para a Lógica Proposicional Intuicionista]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]] * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Estratégias de demonstração]] * [[Relação de consequência]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] 516da3a3d652c2f04df6829b00a52460f83c8087 Dedução Natural para a Lógica de Primeira Ordem Clássica 0 254 1470 1413 2020-10-14T02:09:59Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki * Regras para os ''quantificadores'' e para a ''igualdade'': <!-- --><p>ver [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]]</p> * ''Efeitos das regras clássicas da negação'' sobre os quantificadores<!-- --><p>{{#ev:youtube|8V6u6BrqJ-M}}</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * ac3a0615f79f3bcecf3b5ad169fc23e8dbde9dc3 1493 1470 2020-10-16T00:02:51Z Greati 32 /* Links externos */ wikitext text/x-wiki * Regras para os ''quantificadores'' e para a ''igualdade'': <!-- --><p>ver [[Dedução Natural para a Lógica de Primeira Ordem Intuicionista]]</p> * ''Efeitos das regras clássicas da negação'' sobre os quantificadores<!-- --><p>{{#ev:youtube|8V6u6BrqJ-M}}</p> == Para reflexão == * == Veja também == * [[Quantificadores]] * [[Dedução Natural]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] a27e4acdf4ee600cd5ee5a15d3114d6d36497c0d Modelos (lógica) 0 216 1477 1381 2020-10-15T23:19:42Z Greati 32 /* Links externos */ wikitext text/x-wiki * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a lógica de primeira ordem]] == Para reflexão == * == Veja também == * [[Acarretamento]] (consequência semântica / EN: ''entailment'') * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Acarretamento Acarretamento (Wikipédia)] f45c21922def15ba0ba1ef27bef665cd6ec44d51 Semântica formal para a lógica proposicional 0 255 1478 1401 2020-10-15T23:30:39Z Greati 32 /* Links externos */ wikitext text/x-wiki * Da '''validade de sequentes''' e da '''correção de regras'''<!-- --><p>{{#ev:youtube|bIxOFR0elFo}}</p> * Da '''validade de fórmulas'''<!-- --><p>{{#ev:youtube|GUh5XOjgsWw}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a Lógica Proposicional Clássica]] * [[Semântica formal para a Lógica Proposicional Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_booleana Função booleana (Wikipédia)] * [http://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_de_verdade Função de verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Valor_de_verdade Valor de verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (Wikipédia)] * [http://pt.wikipedia.org/wiki/Validade Validade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Completude_funcional Completude funcional (Wikipédia)] * [http://pt.wikipedia.org/wiki/Completude_%28l%C3%B3gica%29 Completude (Wikipédia)] * [http://pt.wikipedia.org/wiki/Corre%C3%A7%C3%A3o Correção (Wikipédia)] edee90a08522c590cd74ec87e46419223111efb7 Semântica formal para a lógica de primeira ordem 0 256 1479 1438 2020-10-15T23:36:36Z Greati 32 /* Links externos */ wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' ''de fórmulas'' em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Da '''validade''' global ''de um sequente'', e da validade de um sequente em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|8LKRvIX3KkY|||||start=145&end=258&loop=1}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_booleana Função booleana (Wikipédia)] * [http://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_de_verdade Função de verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Valor_de_verdade Valor de verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (Wikipédia)] * [http://pt.wikipedia.org/wiki/Validade Validade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Completude_funcional Completude funcional (Wikipédia)] * [http://pt.wikipedia.org/wiki/Completude_%28l%C3%B3gica%29 Completude (Wikipédia)] * [http://pt.wikipedia.org/wiki/Corre%C3%A7%C3%A3o Correção (Wikipédia)] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica) (Wikipédia)] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_pretendida Interpretação pretendida (Wikipédia)] * [http://pt.wikipedia.org/wiki/Anexo:Lista_de_teorias_de_primeira_ordem Lista de teorias de primeira ordem (Wikipédia)] * [http://pt.wikipedia.org/wiki/Teoria_sem%C3%A2ntica_da_verdade Teoria semântica da verdade (Wikipédia)] 9293b611fb1467b9f0af53f07c35f1dc78c6f52e 1480 1479 2020-10-15T23:37:04Z Greati 32 /* Links externos */ wikitext text/x-wiki * Da '''verdade''' e da '''falsidade''' ''de fórmulas'' em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|j41Sw3dJ1rU}}</p> * Da '''validade''' global ''de um sequente'', e da validade de um sequente em uma estrutura de interpretação específica<!-- --><p>{{#ev:youtube|8LKRvIX3KkY|||||start=145&end=258&loop=1}}</p> == Para reflexão == * == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Intuicionista]] * [[Acarretamento]] * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_booleana Função booleana (Wikipédia)] * [http://pt.wikipedia.org/wiki/Fun%C3%A7%C3%A3o_de_verdade Função de verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Valor_de_verdade Valor de verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (Wikipédia)] * [http://pt.wikipedia.org/wiki/Validade Validade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Completude_funcional Completude funcional (Wikipédia)] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica) (Wikipédia)] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_pretendida Interpretação pretendida (Wikipédia)] * [http://pt.wikipedia.org/wiki/Anexo:Lista_de_teorias_de_primeira_ordem Lista de teorias de primeira ordem (Wikipédia)] * [http://pt.wikipedia.org/wiki/Teoria_sem%C3%A2ntica_da_verdade Teoria semântica da verdade (Wikipédia)] * [http://pt.wikipedia.org/wiki/Completude_%28l%C3%B3gica%29 Completude (Wikipédia)] * [http://pt.wikipedia.org/wiki/Corre%C3%A7%C3%A3o Correção (Wikipédia)] 0f70da92b702cc15ca430b3b34b3da30c03d57a8 Relação de consequência 0 236 1481 1384 2020-10-15T23:38:52Z Greati 32 /* Links externos */ wikitext text/x-wiki * Definição de '''relação de consequência''': versão tarskiana, unilateralista<!-- --><p>{{#ev:youtube|3eEXx4HN3AM}}</p><!-- --><p>Acima, também: '''operação de consequência''', '''teoria''', '''finitariedade''', '''invariância por substituição'''</p> * Noção de '''equivalência lógica'''<!-- --><p>{{#ev:youtube|Et_hGh-XLnM}}</p> * '''Congruencialidade''': enunciado e significado do Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem'')<!-- --><p>{{#ev:youtube|fw8t7Kju3gI}}</p> * Noções de '''inconsistência'''<!-- --><p>{{#ev:youtube|DaBt0ZFVDtE}}</p> == Para reflexão == * Quando podemos dizer que duas ''teorias'' (em uma mesma linguagem) são logicamente equivalentes? == Veja também == * [[Formalismos dedutivos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Correção e completude]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/F%C3%B3rmula_%28l%C3%B3gica%29 Fórmula (lógica) (Wikipédia)] * [http://pt.wikipedia.org/wiki/Rela%C3%A7%C3%A3o_bin%C3%A1ria Relação binária (Wikipédia)] * [http://pt.wikipedia.org/wiki/Consequ%C3%AAncia_l%C3%B3gica Consequência lógica (Wikipédia)] 014a6e8abf9c7fb981f987fa29f37f64869f3166 Sintaxe da lógica proposicional 0 261 1482 1368 2020-10-15T23:41:06Z Greati 32 /* Links externos */ wikitext text/x-wiki * '''Fórmulas'''<!-- --><p>{{#ev:youtube|OqcmQGaJjl0}}</p> * Algumas ''comparações'' entre as linguagens proposicional e de primeira ordem<!-- --><p>{{#ev:youtube|NuMzq-9i25o}}</p> == Para reflexão == * == Veja também == * [[Definição recursiva da linguagem proposicional]] * [[Sintaxe da lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] * [http://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura] * [http://pt.wikipedia.org/wiki/F%C3%B3rmula_%28l%C3%B3gica%29 Fórmula (lógica)] * [http://pt.wikipedia.org/wiki/F%C3%B3rmula_bem_formada Fórmula bem formada] f765d7af6f21234f689f5953c2c28c7885a1b0db 1483 1482 2020-10-15T23:41:29Z Greati 32 /* Links externos */ wikitext text/x-wiki * '''Fórmulas'''<!-- --><p>{{#ev:youtube|OqcmQGaJjl0}}</p> * Algumas ''comparações'' entre as linguagens proposicional e de primeira ordem<!-- --><p>{{#ev:youtube|NuMzq-9i25o}}</p> == Para reflexão == * == Veja também == * [[Definição recursiva da linguagem proposicional]] * [[Sintaxe da lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/Sintaxe_%28l%C3%B3gica%29 Sintaxe (lógica)] * [http://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] * [http://pt.wikipedia.org/wiki/F%C3%B3rmula_%28l%C3%B3gica%29 Fórmula (lógica)] * [http://pt.wikipedia.org/wiki/F%C3%B3rmula_bem_formada Fórmula bem formada] 21f50ee13ff5b18efb8eea66db475f37795e76fd Definição recursiva da linguagem proposicional 0 207 1484 1398 2020-10-15T23:43:25Z Greati 32 /* Links externos */ wikitext text/x-wiki * Como um ''conjunto indutivamente definido''<!-- --><p>{{#ev:youtube|hCCVtDR4RVo}}</p> * Como uma ''álgebra absolutamente livre'' (e também como ''objeto inicial'' da categoria associada)<!-- --><p>{{#ev:youtube|7-wpBM93SyY}}</p> == Para reflexão == * Como deveria ser a definição recursiva estrutural de '''substituição de átomos por fórmulas''', de modo a consistir em um endomorfismo sobre a álgebra das fórmulas?<!-- --><p>Notação: <math>\varphi[p\mapsto \psi]</math> (denotando o resultado de substituir, na fórmula <math>\varphi</math> todas ocorrências do átomo <math>p</math> pela fórmula <math>\psi</math>)</p> == Veja também == * [[Sintaxe da lógica proposicional]] * [[Conjunto indutivamente definido]] * [[Álgebra absolutamente livre]] * [[Álgebra dos termos]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Assinatura_%28l%C3%B3gica%29 Assinatura (lógica)] * [http://pt.wikipedia.org/wiki/F%C3%B3rmula_%28l%C3%B3gica%29 Fórmula (lógica)] * [http://pt.wikipedia.org/wiki/Indu%C3%A7%C3%A3o_estrutural Indução estrutural] 8b6fb81f52db4cc17914897c23828fb084945e6e Formalismos dedutivos 0 217 1485 1383 2020-10-15T23:48:40Z Greati 32 /* Links externos */ wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> * '''Sistemas dedutivos''' particulares: :* no estilo da [[Dedução Natural]] :* no estilo do [[Cálculo de Sequentes]] [AGUARDE!] :* no estilo dos [[Tableaux]] [AGUARDE!] :* no estilo do [[Método da Resolução]] [AGUARDE!] == Para reflexão == * Será possível ''mecanizar'' inteiramente a Matemática? * Como caracterizar o principal objeto de estudos da '''Teoria das Demonstrações''' (EN: ''Proof Theory'')? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Sistema_dedutivo Sistema dedutivo] * [http://pt.wikipedia.org/wiki/Regra_de_infer%C3%AAncia Regra de inferência] * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] c85aab9acfe67bf7e41587d7d6c7857eebd7ee94 1486 1485 2020-10-15T23:49:58Z Greati 32 /* Links externos */ wikitext text/x-wiki * '''Estilos''' de formalismo dedutivo<!-- --><p>{{#ev:youtube|SouCz7oIRk4}}</p> * '''Sistemas dedutivos''' particulares: :* no estilo da [[Dedução Natural]] :* no estilo do [[Cálculo de Sequentes]] [AGUARDE!] :* no estilo dos [[Tableaux]] [AGUARDE!] :* no estilo do [[Método da Resolução]] [AGUARDE!] == Para reflexão == * Será possível ''mecanizar'' inteiramente a Matemática? * Como caracterizar o principal objeto de estudos da '''Teoria das Demonstrações''' (EN: ''Proof Theory'')? * Uma questão terminológica: ''prova'' ou ''demonstração''? [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/S-9hikoKNQs/hiKu9O0-EQAJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/-AgRQtppdNQ/-A31IoAKL40J] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/1PKaK3WzMT0/MU00E9xe8xsJ] [https://groups.google.com/a/dimap.ufrn.br/d/msg/logica-l/PPDpTxuVUWY/3d7cDt6uCXIJ] == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Sistema_dedutivo Sistema dedutivo] * [http://pt.wikipedia.org/wiki/Regra_de_infer%C3%AAncia Regra de inferência] * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] * [https://en.wikipedia.org/wiki/G%C3%B6del%27s_incompleteness_theorems Teoremas de Incompletabilidade de Gödel] 89fb64bae4421965463ba91e65cb177e321055c6 Semântica formal para a Lógica de Primeira Ordem Clássica 0 247 1489 1440 2020-10-15T23:55:43Z Greati 32 /* Links externos */ wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica:<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] a17925c86b10d1e6f791494a031c80db3aafb89b 1497 1489 2020-10-22T18:24:25Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica:<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * Como você procederia para tentar verificar a seguinte igualdade? <math>$[[ t [x \mapsto u ] ]]^{<I,\rho>} = [[ t ]]^{<I,\rho[x := [[u]]^{<I,\rho>} ]>}$</math> * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] 1dfc348e5f6d694e9ab37310be55cfbe05cf5d63 1498 1497 2020-10-22T18:34:17Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica:<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * Como você procederia para tentar verificar a seguinte igualdade? <math>[[ t [x \mapsto u ] ]]^{<I,\rho>} = [[ t ]]^{<I,\rho[x := [[u]]^{<I,\rho>} ]>}</math> * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] fe034a3499975e89de49e79f4a954e57dedfacc1 Quantificadores 0 248 1490 1425 2020-10-15T23:56:50Z Greati 32 /* Links externos */ wikitext text/x-wiki * A '''interpretação substitucional''' vs a '''interpretação objetual'''<!-- --><p>{{#ev:youtube|6jyw92ijy9Q}}</p><!-- --><p>Ainda: ''quantificações ociosas'' e ''renomeamento de variáveis''. Ilustração detalhada da análise semântica recursiva de uma sentença contendo quantificadores.</p><!-- --><p>Como avaliar a adequação de uma tradução da linguagem natural para a linguagem formal.</p> * A ''dualidade'' entre os quantificadores universal e existencial<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4|||||start=598&end=687&loop=1}}</p> * A ''interação'' entre os quantificadores e os conectivos de ''conjunção'' e de ''disjunção''<!-- --><p>{{#ev:youtube|vKxDPQgLRuU}}</p> * ''Aninhamento'' de quantificadores<!-- --><p>{{#ev:youtube|7ihfZ4wlgFw}}</p> * Quantificadores ''relativizados''<!-- --><p>{{#ev:youtube|H3HhormBWr8}}</p> * Quantificadores ''de contagem''<!-- --><p>{{#ev:youtube|Eq1JOPhL9xE}}</p> == Para reflexão == * Além da ''dualidade'', você seria capaz de verificar semanticamente, de maneira detalhada, todas as conexões características do ''Quadrado das Oposições''? * Se a linguagem de primeira ordem possuísse ''somente'' quantificações relativizadas, como você faria para definir a partir destas últimas as quantificações irrestritas da versão ''homogênea'' da linguagem de primeira ordem? * Você seria capaz de verificar em detalhe, usando a semântica da lógica de primeira ordem, que os quantificadores de contagem funcionam ''como especificado''? == Veja também == * [[Semântica formal para a lógica de primeira ordem]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/Quantifica%C3%A7%C3%A3o Quantificação] * [http://pt.wikipedia.org/wiki/Quantifica%C3%A7%C3%A3o_existencia Quantificação existencial] * [http://pt.wikipedia.org/wiki/Quantifica%C3%A7%C3%A3o_universal Quantificação universal] a21d8c40790318c20852d46740f2f7d4bc981176 Correção e completude para a Lógica de Primeira Ordem Clássica 0 251 1494 1411 2020-10-16T05:14:45Z Jmarcos 3 vídeo adicionado wikitext text/x-wiki {{#ev:youtube|zRZmfBlAiv0}} == Para reflexão == * == Veja também == * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [[Dedução Natural para a Lógica de Primeira Ordem Clássica]] * [[Semântica formal para a Lógica de Primeira Ordem Clássica]] fe8ed32e7256f5c23639f8ad2f4d1ef564a3ce0c Semântica formal para a Lógica de Primeira Ordem Clássica 0 247 1499 1498 2020-10-22T18:37:14Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica:<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * Como você procederia para tentar verificar a seguinte igualdade? <math>\llbracket t [x \mapsto u ] \rrbracket^{<I,\rho>} = \llbracket t \rrbracket^{<I,\rho[x := [[u]]^{<I,\rho>} ]>}</math> * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] 69f4cdaf3884e8cd6fa1ac47cc4a6f3a0ee26a53 1500 1499 2020-10-22T18:45:52Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Estruturas de interpretação''' para uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|QL_mxL3_QVg}}</p> * '''Alocações de valores para os símbolos de variável''' sobre uma estrutura de interpretação<!-- --><p>{{#ev:youtube|DvSK3t-7POQ}}</p> * '''Modelos de primeira ordem''', e ''interpretações pretendidas''<!-- --><p>{{#ev:youtube|Tuj0RRKuUSo}}</p> * Alocações ''variantes'': coincidentes a menos de exceções locais<!-- --><p>{{#ev:youtube|WU57rb6AGRE}}</p> * '''Denotações dos termos''' induzidos por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|LO4CZ6yvqfE}}</p> * Noção de '''satisfação''' ''para fórmulas relacionais'' e ''para fórmulas quantificadas'' induzidas por uma assinatura de primeira ordem<!-- --><p>{{#ev:youtube|S4bDWqwCGZ4}}</p> * Interpretação da '''igualdade''' como a ''identidade''<!-- --><p>{{#ev:youtube|RTlRlxsRzjA}}</p> == Noções de acarretamento associadas == * '''Consequência semântica''' para a Lógica de Primeira Ordem Clássica:<!-- --><p>ver [[Acarretamento]]</p> == Para reflexão == * Como você procederia para tentar verificar a seguinte igualdade? <math>[\![ t [x \mapsto u ] ]\!]^{<I,\rho>} = [\![ t ]\!]^{<I,\rho[x := [[u]]^{<I,\rho>} ]>}</math> * O que você acha que deve mudar, na interpretação de fórmulas relacionais, no contexto de uma semântica para a ''Lógica de Primeira Ordem Intuicionista''? O que dizer, também, da interpretação de fórmulas quantificadas, nesta lógica? Por fim, como você acha que a relação de ''desigualdade'' poderia ser afetada, no caso intuicionista? * Como você demonstraria a ''correção'' da regra de congruencialidade do símbolo de igualdade? * Em que situações lhe pareceria viável generalizar o algoritmo das tabelas de verdade, disponível no caso proposicional, para verificar a satisfatibilidade de fórmulas de primeira ordem? == Veja também == * [[Sintaxe da lógica de primeira ordem]] * [[Semântica formal para a lógica de primeira ordem]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Quantificadores]] * [[Exercícios de semântica formal para a Lógica de Primeira Ordem Clássica]] * [[Correção e completude para a Lógica de Primeira Ordem Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [http://pt.wikipedia.org/wiki/L%C3%B3gica_de_primeira_ordem Lógica de Primeira Ordem] * [http://pt.wikipedia.org/wiki/Interpreta%C3%A7%C3%A3o_%28l%C3%B3gica%29 Interpretação (lógica)] * [http://pt.wikipedia.org/wiki/Valora%C3%A7%C3%A3o_%28l%C3%B3gica%29 Valoração (lógica)] d3d90440efcb56e900bbebfafec587bd56cc1856 Semântica formal para a Lógica Proposicional Clássica 0 241 1501 1423 2020-10-28T22:07:55Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' e noção de '''satisfação''' para a Lógica Proposicional Clássica; ''classe de modelos que satisfazem'' uma dada fórmula ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventura necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? * Uma questão terminológica: ''booleano'' ou ''booliano''? [https://tribunadoceara.com.br/blogs/orlando-nunes/gramatica-2/shakespeareano-ou-shakespeariano/]. == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] b87cc4f8641c10cf0efdeecfa20ea07762ff8687 1502 1501 2020-10-28T22:13:47Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' e noção de '''satisfação''' para a Lógica Proposicional Clássica; ''classe de modelos que satisfazem'' uma dada fórmula ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventura necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? * Uma questão terminológica: ''booleano'' ou ''booliano''? [https://tribunadoceara.com.br/blogs/orlando-nunes/gramatica-2/shakespeareano-ou-shakespeariano/]. * Outra questão terminológica: aparentemente o termo ''satisfatível'' ainda não foi dicionarizado ([https://www.flip.pt/Duvidas-Linguisticas/Duvida-Linguistica/DID/1694]). Vamos ter que esperar um pouco mais, mas não há que ficar parado por conta disso! == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] 23410e3283e3488b557b05ee0150e18f265ac481 1503 1502 2020-10-28T22:15:32Z Jmarcos 3 wikitext text/x-wiki == Interpretações == * '''Interpretações boolianas''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|wK8E792poy4}}</p> * '''Semântica de valorações''' e noção de '''satisfação''' para a Lógica Proposicional Clássica; ''classe de modelos que satisfazem'' uma dada fórmula ou conjunto de fórmulas<!-- --><p>{{#ev:youtube|BszIcAW6ftQ}}</p> * O algoritmo das '''tabelas de verdade'''<!-- --><p>{{#ev:youtube|k55iZI4by5Q}}</p> == Noção de acarretamento associada == * '''Consequência semântica''' para a Lógica Proposicional Clássica<!-- --><p>{{#ev:youtube|sS-teSnboTU}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via semântica<!-- --><p>{{#ev:youtube|HG0uRcIw11U}}</p> == Para reflexão == * Você seria capaz de definir ''todas'' as funções auxiliares que sejam porventura necessárias à implementação do algoritmo das tabelas de verdade? * Por que podemos dizer, em geral, que cada linha de uma tabela de verdade representa uma ''classe''de valorações? * Como comprovar que a relação de acarretamento associada à semântica da Lógica Clássica é ''invariante por substituição''? * Uma questão terminológica: ''booleano'' ou ''booliano''? [https://tribunadoceara.com.br/blogs/orlando-nunes/gramatica-2/shakespeareano-ou-shakespeariano/]. * Outra questão terminológica: aparentemente o termo ''satisfatível'' [https://www.flip.pt/Duvidas-Linguisticas/Duvida-Linguistica/DID/1694 ainda não foi dicionarizado]. Vamos ter que esperar um pouco mais, mas não há que ficar parado por conta disso! == Veja também == * [[Semântica formal para a lógica proposicional]] * [[Poder expressivo dos operadores clássicos]] * [[Acarretamento]] (consequência semântica, ''entailment'') * [[Exercícios de semântica formal para a Lógica Proposicional Clássica]] * [[Correção e completude para a Lógica Proposicional Clássica]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * [https://pt.wikipedia.org/wiki/%C3%81lgebra_booliana Álgebra de Boole] b1dc970285f48b1eec9375ff6e90562e1ad4299f Dedução Natural para a Lógica Proposicional Intuicionista 0 238 1504 1391 2021-03-01T19:58:44Z Jmarcos 3 adding links wikitext text/x-wiki * Regras para a '''conjunção'''<!-- --><p>{{#ev:youtube|moh07B8dv2k}}</p> * Regras para a '''implicação''' (intuicionista)<!-- --><p>{{#ev:youtube|mlEYLd56pMg}}</p> * Regras para a '''disjunção'''<!-- --><p>{{#ev:youtube|yUejpYb2NgI}}</p> * Regras para o '''bottom''' (intuicionista) e para o '''top'''<!-- --><p>{{#ev:youtube|jOzs9BAA8E8}}</p> * Regras para a '''negação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|-Exorelokdo}}</p> * Regras para a '''bi-implicação''' intuicionista (primitivas versus derivadas)<!-- --><p>{{#ev:youtube|nhvGKcSH3v0}}</p> * '''Congruencialidade''': Meta-teorema de Substitutividade de Equivalentes (EN: ''replacement theorem''), pela via dedutiva<!-- --><p>{{#ev:youtube|C2S2z-Emw5Y}}</p> == Para reflexão == * == Veja também == * [[Dedução Natural]] * [[Dedução Natural para a Lógica Proposicional Clássica]] * [[Exercícios de Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] == Links externos == * 05a80ea5935fdffb5699246899ad0e18d676c3c0 Main Page 0 45 1513 90 2021-06-30T21:05:18Z Admin 1 Removed redirect to [[Logic Wiki (LoLITA)]] wikitext text/x-wiki =Group for Computability, Approximate Reasoning, Ordered structures, and Logics= CAROL is a research group of Federal University of Rio Grande do Norte (UFRN). It agglutinates researchers who are interested in the mathematical/formal aspects of Computing and its applications. == Matemática Discreta == * [[ Fundamentos Matemáticos da Computação 1]] * [[ Fundamentos Matemáticos da Computação 2]] * [[ Fundamentos Matemáticos da Computação 3]] == Ferramentas de Lógica == * [[ Uma lista de Ferramentas para o Ensino de Lógica]] == Documentação == * [[Moodle - Documentação]] * [[ProofWeb - Documentação]] * [[TryLogic - Documentação]] Consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software. == Getting started == * [https://www.mediawiki.org/wiki/Help:Formatting Help:Formatting] * [https://www.mediawiki.org/wiki/Help:Links Help:Links] * [https://www.mediawiki.org/wiki/Help:VisualEditor/User_guide Help:VisualEditor] * [https://en.wikipedia.org/wiki/List_of_mathematical_symbols List of mathematical symbols] * [[Editing tricks]] * [[Graphs with Mermaid]] 3ea1b119dc902a7e3ce7b8e6b25e1d9d0f5bc279 1514 1513 2021-07-23T14:03:33Z Admin 1 wikitext text/x-wiki =Group for Computability, Approximate Reasoning, Ordered structures, and Logics= CAROL is a research group of Federal University of Rio Grande do Norte (UFRN). It agglutinates researchers who are interested in the mathematical/formal aspects of Computing and its applications. == Matemática Discreta == * [[ Fundamentos Matemáticos da Computação 1]] * [[ Fundamentos Matemáticos da Computação 2]] * [[ Fundamentos Matemáticos da Computação 3]] * [[ Introdução Computacional à Lógica Matemática]] == Ferramentas de Lógica == * [[ Uma lista de Ferramentas para o Ensino de Lógica]] == Documentação == * [[Moodle - Documentação]] * [[ProofWeb - Documentação]] * [[TryLogic - Documentação]] Consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software. == Getting started == * [https://www.mediawiki.org/wiki/Help:Formatting Help:Formatting] * [https://www.mediawiki.org/wiki/Help:Links Help:Links] * [https://www.mediawiki.org/wiki/Help:VisualEditor/User_guide Help:VisualEditor] * [https://en.wikipedia.org/wiki/List_of_mathematical_symbols List of mathematical symbols] * [[Editing tricks]] * [[Graphs with Mermaid]] cdfb416b5329eaa5d275ab2d8b36319d5bffbb33 Introdução Computacional à Lógica Matemática 0 199 1515 1468 2021-07-23T14:05:51Z Admin 1 wikitext text/x-wiki * [[Relação de consequência]] * [[Lógica proposicional]] * [[Lógica de primeira ordem]] * [[Sintaxe (lógica)]] * [[Formalismos dedutivos]] * [[Modelos (lógica)]] == Para reflexão == * == Veja também == * == Links externos == * [https://vitorgreati.me/experiments/logicwiki.html Visualização da estrutura da wiki] * [https://sites.google.com/site/sequiturquodlibet/courses/laac Lógica Aplicada à Computação]: material de estudos, livro didático, exercícios e exames-tipo 936df65498bf802e1060326959dd174afb9248c3 Exercícios de Dedução Natural 0 237 1516 1487 2021-07-23T14:07:16Z Admin 1 /* Dedução Natural para a Lógica de Primeira Ordem Intuicionista */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== [AGUARDE!] === Derivabilidade de sequentes === ==== <math>\neg(\exists x)\neg\varphi \vdash (\forall x)\varphi</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=188}} ==== <math>\vdash (\exists x)(\forall y)(B(y) \lor \neg B(x))</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=460}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==== <math>(\approx_{sim}) \Gamma \vdash t_1 \approx t_2 / \Gamma \vdash t_2 \approx t_1</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=435}} ==== <math>(\approx_{trn}) \Gamma_1 \vdash t_1 \approx t_2; \Gamma_2 \vdash t_2 \approx t_3/ \Gamma_1,\Gamma_2 \vdash t_1 \approx t_3</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=515}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] * [http://pt.wikipedia.org/wiki/Sistema_dedutivo Sistema dedutivo] 8eb0d2b60b8827e95ccd2cd6296160b83e0d9864 1517 1516 2021-07-23T14:07:54Z Admin 1 /* Dedução Natural para a Lógica de Primeira Ordem Clássica */ wikitext text/x-wiki ==Dedução Natural para a Lógica Proposicional Intuicionista== === Derivabilidade de sequentes === ====<math>\varphi \land \psi \vdash \psi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=480&end=527&loop=1}} ====<math>(\varphi \land \psi) \land \delta \vdash \varphi \land (\psi \land \delta)</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=528&end=582&loop=1}} ====<math>\varphi \vdash \varphi \land \varphi</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=583&end=636&loop=1}} ====<math>\alpha \land \beta, \gamma \land \delta \vdash \gamma \land \beta</math>==== : {{#ev:youtube|moh07B8dv2k|||||start=637}} ====<math>\alpha \to (\beta \to \gamma) \vdash \beta \to (\alpha \to \gamma)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=579}} ====<math>\vdash \alpha \to (\beta \to \alpha)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=768}} ====<math>\vdash \alpha \to \alpha</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=888}} ====<math>\alpha \to \beta \vdash \alpha \to (\alpha \to \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=965}} ====<math>\alpha \to (\alpha \to \beta) \vdash \alpha \to \beta</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1015}} ====<math>\gamma \to \alpha, \gamma \to \beta \vdash \gamma \to (\alpha \land \beta)</math>==== : {{#ev:youtube|mlEYLd56pMg|||||start=1098}} ====<math>\beta \lor (\alpha \land \beta) \vdash \beta</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=759}} ====<math>(\alpha \lor \beta) \to \gamma \vdash (\alpha \to \gamma) \land (\beta \to \gamma)</math>==== : {{#ev:youtube|yUejpYb2NgI|||||start=850}} ====<math>\alpha \vdash \neg\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=463}} ====<math>\beta \to \alpha, \beta \to \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=498}} ====<math>\alpha, \neg\alpha \vdash \neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=570}} ====<math>\alpha\lor\beta, \neg\alpha\lor\gamma \vdash \beta\lor\gamma</math>==== : {{#ev:youtube|-Exorelokdo|||||start=596}} ====<math> \alpha\to\beta \vdash \neg\beta\to\neg\alpha</math>==== : {{#ev:youtube|-Exorelokdo|||||start=741}} ====<math>\neg(\alpha \lor \beta) \dashv\vdash \neg\alpha\land\neg\beta</math>==== : {{#ev:youtube|-Exorelokdo|||||start=817}} ===Derivabilidade de regras=== ==== <math>\mathrm{(DNE)}</math> a partir de <math>\mathrm{DN_{int}}</math> + (<math>\mathrm{\bot E_{cls}}</math>) ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=312}} ==== <math>(\mathrm{\bot E_{cls}})</math> a partir de <math>\mathrm{DN_{int}}</math> + <math>\mathrm{(DNE)}</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=402}} ==== <math>(\mathbb{M})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=99}} ==== <math>(\mathbb{R})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=184}} ==== <math>(\mathbb{T})</math> ==== : {{#ev:youtube|jdHxUb2koy8|||||start=226}} ==Dedução Natural para a Lógica Proposicional Clássica== ===Derivabilidade de sequentes=== <!-- *''Derivações na forma de árvores rotuladas com fórmulas'' --> ====Terceiro Excluído / ''Tertium Non Datur'': <math>\vdash\varphi\lor\neg\varphi</math>==== : {{#ev:youtube|kNyjuCFUzC8}}<!-- -->''Tarefa:'' Demonstrar a mesma fórmula, invertendo a ordem de aplicação das regras de introdução da disjunção. ==== <math>\neg\neg\alpha \vdash \alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=499}} ==== <math>\neg\beta\to\alpha, \neg\beta\to\neg\alpha \vdash \beta</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=545}} ==== <math>\neg\alpha\to\neg\beta \vdash \beta\to\alpha</math> ==== : {{#ev:youtube|0RiYJ5EinRE|||||start=596}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via raciocínio por absurdo ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=45}} ==== <math>\vdash (\alpha \to \beta) \lor (\beta \to \alpha)</math>, via terceiro excluído ==== : {{#ev:youtube|9BdeXjhyJWs|||||start=413}} ==Dedução Natural para a Lógica de Primeira Ordem Intuicionista== === Derivabilidade de sequentes === ==== <math>(\forall x)(\varphi \to \psi) \vdash (\forall x)\varphi \to (\forall x)\psi</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1040}} ==== <math>(\forall x)Q(x) \vdash (\forall y)Q(y)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1244}} ==== <math>(\forall x_1)(\forall x_2)R(x_1,x_2) \vdash (\forall x_2)(\forall x_1)R(x_1,x_2)</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1303}} ==== <math>(\forall x) \varphi_1 \land \varphi_2 \dashv\vdash (\forall x) \varphi_1 \land (\forall x)\varphi_2</math> ==== : {{#ev:youtube|B7fFRZF_wao|||||start=1678}} ==== <math>(\exists x)P(x), (\forall x)(\forall y)(P(x) \to Q(y)) \vdash (\forall y) Q(y)</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=810}} ==== <math>(\forall x)A(x), (\exists y)(A(y) \to B(y)), (\forall z)(A(z) \to C(z)) \vdash (\exists w)(B(w) \land C(w))</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1152}} ==== <math>(\exists x)(\varphi_1 \lor \varphi_2) \dashv \vdash (\exists x)\varphi_1 \lor (\exists x)\varphi_2</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1425}} ==== <math>(\exists x)(\forall y) \varphi \vdash (\forall y)(\exists x) \varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1701}} ==== <math>(\forall x)\neg\varphi \vdash \neg(\exists x)\varphi</math> ==== : {{#ev:youtube|C37Y-1vqRAY|||||start=1910}} ==Dedução Natural para a Lógica de Primeira Ordem Clássica== === Derivabilidade de sequentes === ==== <math>\neg(\exists x)\neg\varphi \vdash (\forall x)\varphi</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=188}} ==== <math>\vdash (\exists x)(\forall y)(B(y) \lor \neg B(x))</math> ==== : {{#ev:youtube|8V6u6BrqJ-M|||||start=460}} ===Derivabilidade de regras=== ====Raciocínio por casos: <math>\Gamma_1, \neg\varphi\vdash\psi; \Gamma_2, \varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \psi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||end=141&loop=1}} ====Raciocínio por redução ao absurdo: <math>\Gamma_1, \neg\varphi\vdash\neg\psi; \Gamma_2, \neg\varphi\vdash\psi \, / \, \Gamma_1, \Gamma_2 \vdash \varphi</math>==== : {{#ev:youtube|w-f04Idz-6M|||||start=149&loop=1}} ==== <math>(\approx_{sim}) \Gamma \vdash t_1 \approx t_2 / \Gamma \vdash t_2 \approx t_1</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=435}} ==== <math>(\approx_{trn}) \Gamma_1 \vdash t_1 \approx t_2; \Gamma_2 \vdash t_2 \approx t_3/ \Gamma_1,\Gamma_2 \vdash t_1 \approx t_3</math> ==== : {{#ev:youtube|knltOmL0XEg|||||start=515}} ==Para reflexão== * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra<!-- --><p><math> (\bot \mathrm{E}_{cls}) \; \Gamma, \neg\varphi \vdash \bot\, / \, \Gamma \vdash \varphi </math></p><!-- --><p>adicionarmos uma regra da forma </p><!-- --><p><math> \Gamma, \neg(\alpha \# \beta) \vdash \bot\, / \, \Gamma \vdash \alpha \# \beta </math></p><!-- --><p>para algum conectivo binário <math>\#</math> da nossa linguagem? </p> * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a seguinte regra de ''consequentia mirabilis''?</p><!-- --><p><math> (\neg_{cls}) \; \Gamma, \neg\alpha \vdash \alpha\, / \, \Gamma \vdash \alpha </math></p><!-- --><p>(Será que podemos dizer, neste caso, que se trata de uma regra de introdução ou de eliminação? E quanta diferença isso faz?) * O que ocorre se ao invés de adicionarmos ao sistema de Dedução Natural para a Lógica de Primeira Ordem Intuicionista a regra <math> (\bot \mathrm{E}_{cls}) </math> adicionarmos a regra </p><!-- --><p><math> (DNQ) \; \Gamma \vdash (\forall x)\neg\neg\varphi\, / \, \Gamma \vdash \neg\neg(\forall x)\varphi </math></p> ==Veja também== * [[Dedução Natural]] * [[Estratégias de demonstração]] * [[Introdução Computacional à Lógica Matemática]] ==Links externos== * [http://pt.wikipedia.org/wiki/Dedu%C3%A7%C3%A3o_natural Dedução natural] * [http://pt.wikipedia.org/wiki/Sistema_dedutivo Sistema dedutivo] b234e6e423e6d3c75adc1e881e40404a5d101df6